From 949defe5e6d984813ea3ba3bb1a5a29805daff4b Mon Sep 17 00:00:00 2001 From: alexpeshkoff Date: Wed, 15 Jan 2014 13:02:08 +0000 Subject: [PATCH] Implemented CORE-4317: Make ISQL use new object API with 32-bit length for object sizes (messages, SQL statements, etc.) Implementation also includes changes in GPRE, but this utility is not complete - only commands, used in ISQL, are working in code generator for new API. New interface IUtl is added - it performs tasks, related with database objects (attachment, transaction, etc.), but not requiring routing in YValve, i.e. client only tasks. --- builds/posix/make.rules | 9 +- builds/posix/prefix.linux_amd64 | 2 +- src/auth/SecureRemotePassword/Message.h | 8 +- src/common/MsgMetadata.cpp | 18 +- src/common/MsgMetadata.h | 4 +- src/common/classes/ImplementHelper.h | 10 + src/common/classes/TempFile.cpp | 28 +- src/common/classes/TempFile.h | 3 + src/gpre/boot/gpre_meta_boot.cpp | 6 + src/gpre/gpre.cpp | 12 + src/gpre/gpreswi.h | 2 + src/gpre/lang_proto.h | 1 + src/gpre/obj_cxx.cpp | 3975 +++++++++++++++++++++++ src/include/firebird/Interface.h | 4 +- src/include/firebird/Provider.h | 9 +- src/include/firebird/Utl.h | 67 + src/isql/extract.epp | 124 +- src/isql/isql.epp | 2324 ++++++------- src/isql/isql.h | 38 +- src/isql/isql_proto.h | 11 +- src/isql/show.epp | 495 ++- src/isql/show_proto.h | 3 +- src/msgs/facilities2.sql | 2 +- src/msgs/messages2.sql | 11 + src/remote/client/BlrFromMessage.cpp | 2 +- src/yvalve/MasterImplementation.cpp | 16 + src/yvalve/MasterImplementation.h | 1 + src/yvalve/perf.cpp | 219 +- src/yvalve/prepa_proto.h | 12 +- src/yvalve/preparse.cpp | 87 +- src/yvalve/utl.cpp | 1046 +++--- src/yvalve/utl_proto.h | 28 + src/yvalve/why.cpp | 111 +- src/yvalve/why_proto.h | 6 + 34 files changed, 6499 insertions(+), 2195 deletions(-) create mode 100644 src/gpre/obj_cxx.cpp create mode 100644 src/include/firebird/Utl.h diff --git a/builds/posix/make.rules b/builds/posix/make.rules index 4cd7ea5d96..9ab7d924b5 100644 --- a/builds/posix/make.rules +++ b/builds/posix/make.rules @@ -62,7 +62,7 @@ WCXXFLAGS:= $(WFLAGS) $(THR_FLAGS) $(RTTI_FLAG) $(CXXFLAGS) $(GLOB_OPTIONS) GPRE_FLAGS= -m -z -n JRD_GPRE_FLAGS = -n -z -gds_cxx -ids -DSQL_GPRE_FLAGS = -m -z -n +ISQL_GPRE_FLAGS = -m -z -n -ocxx .SUFFIXES: .c .e .epp .cpp @@ -74,11 +74,8 @@ DSQL_GPRE_FLAGS = -m -z -n $(OBJ)/jrd/%.cpp: $(SRC_ROOT)/jrd/%.epp $(GPRE_CURRENT) $(JRD_GPRE_FLAGS) $(firstword $<) $@ -$(OBJ)/dsql/%.cpp: $(SRC_ROOT)/dsql/%.epp - $(GPRE_CURRENT) $(DSQL_GPRE_FLAGS) $< $@ - -$(OBJ)/yvalve/%.cpp: $(SRC_ROOT)/yvalve/%.epp - $(GPRE_CURRENT) $(DSQL_GPRE_FLAGS) $< $@ +$(OBJ)/isql/%.cpp: $(SRC_ROOT)/isql/%.epp + $(GPRE_CURRENT) $(ISQL_GPRE_FLAGS) $< $@ $(OBJ)/%.cpp: $(SRC_ROOT)/%.epp $(GPRE_CURRENT) $(GPRE_FLAGS) $(firstword $<) $@ diff --git a/builds/posix/prefix.linux_amd64 b/builds/posix/prefix.linux_amd64 index 459212a3b2..bb3b9e6411 100644 --- a/builds/posix/prefix.linux_amd64 +++ b/builds/posix/prefix.linux_amd64 @@ -24,4 +24,4 @@ WARN_FLAGS=-Wall -Wno-switch -Wno-parentheses -Wno-unknown-pragmas -Wno-unused-v PROD_FLAGS=$(COMMON_FLAGS) $(OPTIMIZE_FLAGS) #DEV_FLAGS=-DUSE_VALGRIND $(COMMON_FLAGS) $(WARN_FLAGS) -DEV_FLAGS=$(COMMON_FLAGS) $(WARN_FLAGS) +DEV_FLAGS=$(COMMON_FLAGS) $(WARN_FLAGS) -fmax-errors=8 diff --git a/src/auth/SecureRemotePassword/Message.h b/src/auth/SecureRemotePassword/Message.h index d0b51bcbcf..bb4858e7fe 100644 --- a/src/auth/SecureRemotePassword/Message.h +++ b/src/auth/SecureRemotePassword/Message.h @@ -92,6 +92,7 @@ private: }; +// With template magic, we make the fields strongly-typed. template <> bool Message::checkType(unsigned t, unsigned sz) { @@ -104,6 +105,12 @@ bool Message::checkType(unsigned t, unsigned sz) return (t == SQL_BLOB || t == SQL_QUAD) && sz == sizeof(ISC_QUAD); } +template <> +bool Message::checkType(unsigned t, unsigned sz) +{ + return t == SQL_INT64 && sz == sizeof(ISC_INT64); +} + template <> bool Message::checkType(unsigned t, unsigned sz) { @@ -111,7 +118,6 @@ bool Message::checkType(unsigned t, unsigned sz) } -// With template magic, we make the fields strongly-typed. template class Field { diff --git a/src/common/MsgMetadata.cpp b/src/common/MsgMetadata.cpp index a07d9413c6..72e44c0e67 100644 --- a/src/common/MsgMetadata.cpp +++ b/src/common/MsgMetadata.cpp @@ -81,7 +81,7 @@ public: } } - virtual void FB_CARG setSubType(IStatus* status, unsigned index, unsigned subType) + virtual void FB_CARG setSubType(IStatus* status, unsigned index, int subType) { try { @@ -162,6 +162,22 @@ public: } } + virtual void FB_CARG remove(IStatus* status, unsigned index) + { + try + { + MutexLockGuard g(mtx, FB_FUNCTION); + + indexError(index, "remove"); + + msgMetadata->items.remove(index); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + } + virtual void FB_CARG moveNameToIndex(IStatus* status, const char* name, unsigned index) { try diff --git a/src/common/MsgMetadata.h b/src/common/MsgMetadata.h index 1b843d4eb6..70cf59be52 100644 --- a/src/common/MsgMetadata.h +++ b/src/common/MsgMetadata.h @@ -79,7 +79,7 @@ public: string owner; string alias; unsigned type; - unsigned subType; + int subType; unsigned length; int scale; unsigned charSet; @@ -164,7 +164,7 @@ public: return false; } - virtual unsigned FB_CARG getSubType(IStatus* status, unsigned index) const + virtual int FB_CARG getSubType(IStatus* status, unsigned index) const { if (index < items.getCount()) return items[index].subType; diff --git a/src/common/classes/ImplementHelper.h b/src/common/classes/ImplementHelper.h index 956b25c44c..0492f57bd2 100644 --- a/src/common/classes/ImplementHelper.h +++ b/src/common/classes/ImplementHelper.h @@ -250,6 +250,16 @@ public: }; +// Misc utl access +class UtlInterfacePtr : public AccessAutoInterface +{ +public: + UtlInterfacePtr() + : AccessAutoInterface(getMasterInterface()->getUtlInterface()) + { } +}; + + // When process exits, dynamically loaded modules (for us plugin modules) // are unloaded first. As the result all global variables in plugin are already destroyed // when yvalve is starting fb_shutdown(). This causes almost unavoidable segfault. diff --git a/src/common/classes/TempFile.cpp b/src/common/classes/TempFile.cpp index 2060d9fc57..d10b0b45f8 100644 --- a/src/common/classes/TempFile.cpp +++ b/src/common/classes/TempFile.cpp @@ -70,7 +70,7 @@ static const size_t MAX_TRIES = 256; // we need a class here only to return memory on shutdown and avoid // false memory leak reports -static Firebird::InitInstance zeros; +static InitInstance zeros; // // TempFile::getTempPath @@ -126,6 +126,32 @@ PathName TempFile::create(const PathName& prefix, const PathName& directory) return filename; } +// +// TempFile::create +// +// Creates a temporary file and returns its name +// In error case store exception in status arg +// + +PathName TempFile::create(IStatus* status, const PathName& prefix, const PathName& directory) +{ + PathName filename; + + try { + TempFile file(*getDefaultMemoryPool(), prefix, directory, false); + filename = file.getName(); + } + catch (const Exception& ex) + { + if (status) + { + ex.stuffException(status); + } + } + + return filename; +} + // // TempFile::init // diff --git a/src/common/classes/TempFile.h b/src/common/classes/TempFile.h index 174735bbfa..f365efdba3 100644 --- a/src/common/classes/TempFile.h +++ b/src/common/classes/TempFile.h @@ -29,6 +29,8 @@ namespace Firebird { +class IStatus; + class TempFile : public File { public: @@ -66,6 +68,7 @@ public: static PathName getTempPath(); static PathName create(const PathName& prefix, const PathName& directory = ""); + static PathName create(IStatus* status, const PathName& prefix, const PathName& directory = ""); private: void init(const PathName&, const PathName&); diff --git a/src/gpre/boot/gpre_meta_boot.cpp b/src/gpre/boot/gpre_meta_boot.cpp index ce07479623..f5a1fb5604 100644 --- a/src/gpre/boot/gpre_meta_boot.cpp +++ b/src/gpre/boot/gpre_meta_boot.cpp @@ -765,6 +765,12 @@ public: { return -1; } + + virtual IUtl* FB_CARG getUtlInterface() + { + fb_assert(false); + return NULL; + } }; diff --git a/src/gpre/gpre.cpp b/src/gpre/gpre.cpp index 7c76182fca..7bb17c13b5 100644 --- a/src/gpre/gpre.cpp +++ b/src/gpre/gpre.cpp @@ -468,6 +468,18 @@ int main(int argc, char* argv[]) gpreGlob.database_name = "gds_database"; break; + case IN_SW_GPRE_OCXX: + gen_routine = OBJ_CXX_action; + gpreGlob.sw_language = lang_cxx; + gpreGlob.ident_pattern = "fb_%d"; + gpreGlob.long_ident_pattern = "fb_%ld"; + gpreGlob.utility_name = "fbUtility"; + gpreGlob.count_name = "fbCount"; + gpreGlob.slack_name = "fbSlack"; + gpreGlob.transaction_name = "fbTrans"; + gpreGlob.database_name = "fbDatabase"; + break; + case IN_SW_GPRE_D: // allocate database block and link to db chain diff --git a/src/gpre/gpreswi.h b/src/gpre/gpreswi.h index 1bdfe15be4..ebedfdc2ca 100644 --- a/src/gpre/gpreswi.h +++ b/src/gpre/gpreswi.h @@ -74,6 +74,7 @@ enum gpre_cmd_switch IN_SW_GPRE_D_FLOAT, // use blr_d_float for doubles IN_SW_GPRE_CXX, // source is C++ IN_SW_GPRE_SCXX, // source is C++ with Saber extension + IN_SW_GPRE_OCXX, // c++ with object API IN_SW_GPRE_SQLDA, // use old or new SQLDA IN_SW_GPRE_USER, // default username to use when attaching database IN_SW_GPRE_PASSWORD, // default password to use in attaching database @@ -167,6 +168,7 @@ const static Switches::in_sw_tab_t gpre_in_sw_table[] = {IN_SW_GPRE_M , 0, "MANUAL" , 0, 0, 0, false, 0, 0, "\t\tdo not automatically ATTACH to a database"}, {IN_SW_GPRE_N , 0, "NO_LINES" , 0, 0, 0, false, 0, 0, "\tdo not generate C debug lines"}, {IN_SW_GPRE_O , 0, "OUTPUT" , 0, 0, 0, false, 0, 0, "\t\tsend output to standard out"}, + {IN_SW_GPRE_OCXX , 0, "OCXX" , 0, 0, 0, false, 0, 0, "\t\textended C++ program with objects API"}, #ifdef GPRE_PASCAL {IN_SW_GPRE_P , 0, "PASCAL" , 0, 0, 0, false, 0, 0, "\t\textended PASCAL program"}, #endif diff --git a/src/gpre/lang_proto.h b/src/gpre/lang_proto.h index d012b1baab..9e98938e29 100644 --- a/src/gpre/lang_proto.h +++ b/src/gpre/lang_proto.h @@ -38,6 +38,7 @@ void FTN_fini(); void FTN_print_buffer(TEXT*); void INT_action(const act*, int); void INT_CXX_action(const act*, int); +void OBJ_CXX_action(const act*, int); void PAS_action(const act*, int); //int PLI_action(ACT, int); void RMC_action(const act*, int); diff --git a/src/gpre/obj_cxx.cpp b/src/gpre/obj_cxx.cpp new file mode 100644 index 0000000000..f74aeba7a6 --- /dev/null +++ b/src/gpre/obj_cxx.cpp @@ -0,0 +1,3975 @@ +//____________________________________________________________ +// +// PROGRAM: C preprocess +// MODULE: c_cxx.cpp +// DESCRIPTION: C and C++ code generator +// +// 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): ______________________________________.16/09/2003 +// TMN (Mike Nordell) 11.APR.2001 - Reduce compiler warnings +// +// 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port +// +// +//____________________________________________________________ +// +// + +#include "firebird.h" +#include +#include +#include +#include "../jrd/ibase.h" +#include "../gpre/gpre.h" +#include "../gpre/pat.h" +#include "../gpre/msc_proto.h" +#include "../gpre/cmp_proto.h" +#include "../gpre/gpre_proto.h" +#include "../gpre/lang_proto.h" +#include "../gpre/pat_proto.h" +#include "../common/prett_proto.h" +#include "../yvalve/gds_proto.h" +#include "../common/utils_proto.h" + + +static void align(int); +static void asgn_from(const act*, ref*, int); +static void asgn_to(const act*, ref*, int); +static void asgn_to_proc(const ref*, int); +static void gen_any(const act*, int); +static void gen_at_end(const act*, int); +static void gen_based(const act*, int); +static void gen_blob_close(const act*, USHORT); +static void gen_blob_end(const act*, USHORT); +static void gen_blob_for(const act*, USHORT); +static void gen_blob_open(const act*, USHORT); +static void gen_blr(void*, SSHORT, const char*); +static void gen_clear_handles(int); +//static void gen_compatibility_symbol(const TEXT*, const TEXT*, const TEXT*); +static void gen_compile(const act*, int); +static void gen_create_database(const act*, int); +static int gen_cursor_close(const act*, const gpre_req*, int); +static void gen_cursor_init(const act*, int); +static int gen_cursor_open(const act*, const gpre_req*, int); +static void gen_database(int); +static void gen_ddl(const act*, int); +static void gen_drop_database(const act*, int); +static void gen_dyn_close(const act*, int); +static void gen_dyn_declare(const act*, int); +static void gen_dyn_describe(const act*, int, bool); +static void gen_dyn_execute(const act*, int); +static void gen_dyn_fetch(const act*, int); +static void gen_dyn_immediate(const act*, int); +static void gen_dyn_insert(const act*, int); +static void gen_dyn_open(const act*, int); +static void gen_dyn_prepare(const act*, int); +static void gen_emodify(const act*, int); +static void gen_estore(const act*, int); +static void gen_endfor(const act*, int); +static void gen_erase(const act*, int); +static SSHORT gen_event_block(act*); +static void gen_event_init(const act*, int); +static void gen_event_wait(const act*, int); +static void gen_fetch(const act*, int); +static void gen_finish(const act*, int); +static void gen_for(const act*, int); +static void gen_function(const act*, int); +static void gen_get_or_put_slice(const act*, const ref*, bool, int); +static void gen_get_segment(const act*, int); +static void gen_loop(const act*, int); +static TEXT* gen_name(char* const, const ref*, bool); +static void gen_on_error(const act*, USHORT); +static void gen_procedure(const act*, int); +static void gen_put_segment(const act*, int); +static void gen_raw(const UCHAR*, int); +static void gen_ready(const act*, int); +static void gen_receive(const act*, int, const gpre_port*); +static void gen_release(const act*, int); +static void gen_request(const gpre_req*); +static void gen_return_value(const act*, int); +static void gen_routine(const act*, int); +static void gen_s_end(const act*, int); +static void gen_s_fetch(const act*, int); +static void gen_s_start(const act*, int); +static void gen_segment(const act*, int); +static void gen_select(const act*, int); +static void gen_send(const act*, const gpre_port*, int); +static void gen_slice(const act*, const ref*, int); +static void gen_start(const act*, const gpre_port*, int, bool); +static void gen_store(const act*, int); +static void gen_t_start(const act*, int); +static void gen_tpb(const tpb*, int); +static void gen_trans(const act*, int); +static void gen_type(const act*, int); +static void gen_update(const act*, int); +static void gen_variable(const act*, int); +static void gen_whenever(const swe*, int); +static void make_array_declaration(ref*); +static TEXT* make_name(TEXT* const, const gpre_sym*); +static void make_ok_test(const act*, const gpre_req*, int); +static void make_port(const gpre_port*, int); +static void make_ready(const gpre_dbb*, const TEXT*, const TEXT*, USHORT, const gpre_req*); +static void printa(int, const char*, ...) ATTRIBUTE_FORMAT(2,3); +static void printb(const TEXT*, ...) ATTRIBUTE_FORMAT(1,2); +static const TEXT* request_trans(const act*, const gpre_req*); +static const TEXT* status_vector(const act*); +static void t_start_auto(const act*, const gpre_req*, const TEXT*, int, bool); + +static bool global_first_flag = false; +static const TEXT* global_status_name = 0; + +const int INDENT = 3; + +static const char* const NULL_STRING = "NULL"; +static const char* const NULL_STATUS = "NULL"; +static const char* const NULL_SQLDA = "NULL"; + +#ifdef DARWIN +static const char* const GDS_INCLUDE = ""; +#else +static const char* const GDS_INCLUDE = ""; +#endif + +static const char* const DCL_LONG = "ISC_LONG"; +static const char* const DCL_QUAD = "ISC_QUAD"; + +static inline void begin(const int column) +{ + printa(column, "{"); +} + +static inline void endp(const int column) +{ + printa(column, "}"); +} + +static inline void set_sqlcode(const act* action, const int column) +{ + if (action->act_flags & ACT_sql) + printa(column, "SQLCODE = isc_sqlcode(%s->get());", global_status_name); +} + +static inline void ObjectNotImplemented() +{ + CPR_error("Feature is not implemented for object API"); + throw Firebird::LongJump(); +} + +//____________________________________________________________ +// +// + +void OBJ_CXX_action(const act* action, int column) +{ + + global_status_name = "fbStatus"; + + // Put leading braces where required + + switch (action->act_type) + { + case ACT_alter_database: + case ACT_alter_domain: + case ACT_alter_index: + case ACT_alter_table: + case ACT_blob_close: + case ACT_blob_create: + case ACT_blob_for: + case ACT_blob_open: + case ACT_clear_handles: + case ACT_close: + case ACT_commit: + case ACT_commit_retain_context: + case ACT_create_database: + case ACT_create_domain: + case ACT_create_generator: + case ACT_create_index: + case ACT_create_shadow: + case ACT_create_table: + case ACT_create_view: + case ACT_declare_filter: + case ACT_declare_udf: + case ACT_disconnect: + case ACT_drop_database: + case ACT_drop_domain: + case ACT_drop_filter: + case ACT_drop_index: + case ACT_drop_shadow: + case ACT_drop_table: + case ACT_drop_udf: + case ACT_drop_view: + case ACT_dyn_close: + case ACT_dyn_cursor: + case ACT_dyn_describe: + case ACT_dyn_describe_input: + case ACT_dyn_execute: + case ACT_dyn_fetch: + case ACT_dyn_grant: + case ACT_dyn_immediate: + case ACT_dyn_insert: + case ACT_dyn_open: + case ACT_dyn_prepare: + case ACT_dyn_revoke: + case ACT_fetch: + case ACT_finish: + case ACT_for: + case ACT_get_segment: + case ACT_get_slice: + case ACT_insert: + case ACT_loop: + case ACT_modify: + case ACT_open: + case ACT_prepare: + case ACT_procedure: + case ACT_put_slice: + case ACT_ready: + case ACT_release: + case ACT_rfinish: + case ACT_rollback: + case ACT_rollback_retain_context: + case ACT_s_fetch: + case ACT_s_start: + case ACT_select: + case ACT_store: + case ACT_start: + case ACT_update: + case ACT_statistics: + begin(column); + } + + switch (action->act_type) + { + case ACT_alter_database: + case ACT_alter_domain: + case ACT_alter_index: + case ACT_alter_table: + gen_ddl(action, column); + break; + case ACT_any: + gen_any(action, column); + return; + case ACT_at_end: + gen_at_end(action, column); + return; + case ACT_b_declare: + gen_database(column); + gen_routine(action, column); + return; + case ACT_basedon: + gen_based(action, column); + return; + case ACT_blob_cancel: + gen_blob_close(action, (USHORT) column); + return; + case ACT_blob_close: + gen_blob_close(action, (USHORT) column); + break; + case ACT_blob_create: + gen_blob_open(action, (USHORT) column); + break; + case ACT_blob_for: + gen_blob_for(action, (USHORT) column); + return; + case ACT_blob_handle: + gen_segment(action, column); + return; + case ACT_blob_open: + gen_blob_open(action, (USHORT) column); + break; + case ACT_clear_handles: + gen_clear_handles(column); + break; + case ACT_close: + gen_s_end(action, column); + break; + case ACT_commit: + gen_trans(action, column); + break; + case ACT_commit_retain_context: + gen_trans(action, column); + break; + case ACT_create_database: + gen_create_database(action, column); + break; + case ACT_create_domain: + case ACT_create_generator: + case ACT_create_index: + case ACT_create_shadow: + case ACT_create_table: + case ACT_create_view: + gen_ddl(action, column); + break; + case ACT_cursor: + gen_cursor_init(action, column); + return; + case ACT_database: + gen_database(column); + return; + case ACT_declare_filter: + case ACT_declare_udf: + gen_ddl(action, column); + break; + case ACT_disconnect: + gen_finish(action, column); + break; + case ACT_drop_database: + gen_drop_database(action, column); + break; + case ACT_drop_domain: + case ACT_drop_filter: + case ACT_drop_index: + case ACT_drop_shadow: + case ACT_drop_table: + case ACT_drop_udf: + case ACT_drop_view: + gen_ddl(action, column); + break; + case ACT_dyn_close: + gen_dyn_close(action, column); + break; + case ACT_dyn_cursor: + gen_dyn_declare(action, column); + break; + case ACT_dyn_describe: + gen_dyn_describe(action, column, false); + break; + case ACT_dyn_describe_input: + gen_dyn_describe(action, column, true); + break; + case ACT_dyn_execute: + gen_dyn_execute(action, column); + break; + case ACT_dyn_fetch: + gen_dyn_fetch(action, column); + break; + case ACT_dyn_grant: + gen_ddl(action, column); + break; + case ACT_dyn_immediate: + gen_dyn_immediate(action, column); + break; + case ACT_dyn_insert: + gen_dyn_insert(action, column); + break; + case ACT_dyn_open: + gen_dyn_open(action, column); + break; + case ACT_dyn_prepare: + gen_dyn_prepare(action, column); + break; + case ACT_dyn_revoke: + gen_ddl(action, column); + break; + case ACT_endblob: + gen_blob_end(action, (USHORT) column); + return; + case ACT_enderror: + column += INDENT; + endp(column); + column -= INDENT; + break; + case ACT_endfor: + gen_endfor(action, column); + break; + case ACT_endmodify: + gen_emodify(action, column); + break; + case ACT_endstore: + gen_estore(action, column); + break; + case ACT_erase: + gen_erase(action, column); + return; + case ACT_event_init: + gen_event_init(action, column); + break; + case ACT_event_wait: + gen_event_wait(action, column); + break; + case ACT_fetch: + gen_fetch(action, column); + break; + case ACT_finish: + gen_finish(action, column); + break; + case ACT_for: + gen_for(action, column); + return; + case ACT_function: + gen_function(action, column); + return; + case ACT_get_segment: + gen_get_segment(action, column); + break; + case ACT_get_slice: + gen_slice(action, 0, column); + break; + case ACT_hctef: + endp(column); + break; + case ACT_insert: + gen_s_start(action, column); + break; + case ACT_loop: + gen_loop(action, column); + break; + case ACT_on_error: + gen_on_error(action, (USHORT) column); + return; + case ACT_open: + gen_s_start(action, column); + break; + case ACT_prepare: + gen_trans(action, column); + break; + case ACT_procedure: + gen_procedure(action, column); + break; + case ACT_put_segment: + gen_put_segment(action, column); + break; + case ACT_put_slice: + gen_slice(action, 0, column); + break; + case ACT_ready: + gen_ready(action, column); + break; + case ACT_release: + gen_release(action, column); + break; + case ACT_rfinish: + gen_finish(action, column); + break; + case ACT_rollback: + gen_trans(action, column); + break; + case ACT_rollback_retain_context: + gen_trans(action, column); + break; + case ACT_routine: + gen_routine(action, column); + return; + case ACT_s_end: + gen_s_end(action, column); + return; + case ACT_s_fetch: + gen_s_fetch(action, column); + return; + case ACT_s_start: + gen_s_start(action, column); + break; + case ACT_segment: + gen_segment(action, column); + return; + case ACT_segment_length: + gen_segment(action, column); + return; + case ACT_select: + gen_select(action, column); + break; + case ACT_sql_dialect: + gpreGlob.sw_sql_dialect = ((set_dialect*) action->act_object)->sdt_dialect; + return; + case ACT_start: + gen_t_start(action, column); + break; + case ACT_statistics: + gen_ddl(action, column); + break; + case ACT_store: + gen_store(action, column); + return; + case ACT_store2: + gen_return_value(action, column); + return; + case ACT_type_number: + gen_type(action, column); + return; + case ACT_update: + gen_update(action, column); + break; + case ACT_variable: + gen_variable(action, column); + return; + default: + return; + } + + // Put in a trailing brace for those actions still with us + + if (action->act_flags & ACT_sql) + gen_whenever(action->act_whenever, column); + + if (action->act_error) + fprintf(gpreGlob.out_file, ";"); + else + endp(column); +} + + +//____________________________________________________________ +// +// Align output to a specific column for output. If the +// column is negative, don't do anything. +// + +static void align( int column) +{ + if (column < 0) + return; + + putc('\n', gpreGlob.out_file); + + for (int i = column / 8; i; --i) + putc('\t', gpreGlob.out_file); + + for (int i = column % 8; i; --i) + putc(' ', gpreGlob.out_file); +} + + +//____________________________________________________________ +// +// Build an assignment from a host language variable to +// a port variable. The string assignments are a little +// hairy because the normal mode is varying (null +// terminated) strings, but the fixed subtype makes the +// string a byte stream. Furthering the complication, a +// single character byte stream is handled as a single byte, +// meaining that it is the byte, not the address of the +// byte. +// + +static void asgn_from( const act* action, ref* reference, int column) +{ + TEXT name[MAX_REF_SIZE], variable[MAX_REF_SIZE], temp[MAX_REF_SIZE]; + + for (; reference; reference = reference->ref_next) + { + bool slice_flag = false; + const gpre_fld* field = reference->ref_field; + if (field->fld_array_info) + { + act* slice_action; + ref* source = reference->ref_friend; + if (source && (slice_action = (act*) source->ref_slice) && slice_action->act_object) + { + slice_flag = true; + slice_action->act_type = ACT_put_slice; + gen_slice(slice_action, 0, column); + } + else if (!(reference->ref_flags & REF_array_elem)) + { + printa(column, "%s = fbBlobNull;", gen_name(name, reference, true)); + gen_get_or_put_slice(action, reference, false, column); + continue; + } + } + if (!reference->ref_source && !reference->ref_value && !slice_flag) + continue; + align(column); + gen_name(variable, reference, true); + const TEXT* value; + if (slice_flag) + value = gen_name(temp, reference->ref_friend, true); + else if (reference->ref_source) + value = gen_name(temp, reference->ref_source, true); + else + value = reference->ref_value; + + if (!slice_flag && reference->ref_value && (reference->ref_flags & REF_array_elem)) + { + field = field->fld_array; + } + if (field && field->fld_dtype <= dtype_cstring) + { + if (field->fld_sub_type == 1) + if (field->fld_length == 1) + fprintf(gpreGlob.out_file, "%s = %s;", variable, value); + else + fprintf(gpreGlob.out_file, "isc_ftof (%s, sizeof(%s), %s, %d);", value, + value, variable, field->fld_length); + else if (field->fld_flags & FLD_dbkey) + fprintf(gpreGlob.out_file, "isc_ftof (%s, %d, %s, %d);", value, + field->fld_length, variable, field->fld_length); + else if (gpreGlob.sw_cstring) + fprintf(gpreGlob.out_file, isLangCpp(gpreGlob.sw_language) ? + "isc_vtov ((const char*) %s, (char*) %s, %d);" : + "isc_vtov ((char*) %s, (char*) %s, %d);", + value, variable, field->fld_length); + else if (reference->ref_source) + fprintf(gpreGlob.out_file, "isc_ftof (%s, sizeof(%s), %s, %d);", + value, value, variable, field->fld_length); + else + fprintf(gpreGlob.out_file, "isc_vtof (%s, %s, %d);", + value, variable, field->fld_length); + } + else if (!reference->ref_master || (reference->ref_flags & REF_literal)) + { + fprintf(gpreGlob.out_file, "%s = %s;", variable, value); + } + else + { + fprintf(gpreGlob.out_file, "if (%s < 0)", value); + align(column + 4); + fprintf(gpreGlob.out_file, "%s = -1;", variable); + align(column); + fprintf(gpreGlob.out_file, "else"); + align(column + 4); + fprintf(gpreGlob.out_file, "%s = 0;", variable); + } + } +} + +//____________________________________________________________ +// +// Build an assignment to a host language variable from +// a port variable. +// + +static void asgn_to( const act* action, ref* reference, int column) +{ + char s[MAX_REF_SIZE]; + + ref* source = reference->ref_friend; + const gpre_fld* field = source->ref_field; + + if (field) + { + act* slice_action; + if (field->fld_array_info && (slice_action = (act*) source->ref_slice)) + { + source->ref_value = reference->ref_value; + if (slice_action->act_object) + { + slice_action->act_type = ACT_get_slice; + gen_slice(slice_action, source, column); + } + else + gen_get_or_put_slice(action, source, true, column); + + // Pick up NULL value if one is there + + if (reference = reference->ref_null) + { + align(column); + fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, + gen_name(s, reference, true)); + } + return; + } + + gen_name(s, source, true); + if (field->fld_dtype > dtype_cstring) + fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, s); + else if (field->fld_sub_type == 1 && field->fld_length == 1) + fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, s); + else if (field->fld_flags & FLD_dbkey) + fprintf(gpreGlob.out_file, "isc_ftof (%s, %d, %s, %d);", + s, field->fld_length, reference->ref_value, field->fld_length); + else if (!gpreGlob.sw_cstring || field->fld_sub_type == 1) + fprintf(gpreGlob.out_file, "isc_ftof (%s, %d, %s, sizeof(%s));", + s, field->fld_length, reference->ref_value, reference->ref_value); + else + fprintf(gpreGlob.out_file, isLangCpp(gpreGlob.sw_language) ? + "isc_vtov ((const char*) %s, (char*) %s, sizeof(%s));" : + "isc_vtov ((char*) %s, (char*) %s, sizeof(%s));", + s, reference->ref_value, reference->ref_value); + } + + // Pick up NULL value if one is there + + if (reference = reference->ref_null) + { + align(column); + fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, gen_name(s, reference, true)); + } +} + + +//____________________________________________________________ +// +// Build an assignment to a host language variable from +// a port variable. +// + +static void asgn_to_proc(const ref* reference, int column) +{ + char s[MAX_REF_SIZE]; + + for (; reference; reference = reference->ref_next) + { + if (!reference->ref_value) + continue; + const gpre_fld* field = reference->ref_field; + gen_name(s, reference, true); + align(column); + + if (field->fld_dtype > dtype_cstring) + fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, s); + else if (field->fld_sub_type == 1 && field->fld_length == 1) + fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, s); + else if (field->fld_flags & FLD_dbkey) + fprintf(gpreGlob.out_file, "isc_ftof (%s, %d, %s, %d);", + s, field->fld_length, reference->ref_value, field->fld_length); + else if (!gpreGlob.sw_cstring || field->fld_sub_type == 1) + fprintf(gpreGlob.out_file, "isc_ftof (%s, %d, %s, sizeof(%s));", + s, field->fld_length, reference->ref_value, reference->ref_value); + else + fprintf(gpreGlob.out_file, isLangCpp(gpreGlob.sw_language) ? + "isc_vtov ((const char*) %s, (char*) %s, sizeof(%s));" : + "isc_vtov ((char*) %s, (char*) %s, sizeof(%s));", + s, reference->ref_value, reference->ref_value); + } +} + + +//____________________________________________________________ +// +// Generate a function call for free standing ANY. Somebody else +// will need to generate the actual function. +// + +static void gen_any( const act* action, int column) +{ + align(column); + gpre_req* request = action->act_request; + + fprintf(gpreGlob.out_file, "%s_r (&%s, &%s", + request->req_handle, request->req_handle, request->req_trans); + + gpre_port* port = request->req_vport; + if (port) + for (ref* reference = port->por_references; reference; reference = reference->ref_next) + { + fprintf(gpreGlob.out_file, ", %s", reference->ref_value); + } + + fprintf(gpreGlob.out_file, ")"); +} + + +//____________________________________________________________ +// +// Generate code for AT END clause of FETCH. +// + +static void gen_at_end( const act* action, int column) +{ + char s[MAX_REF_SIZE]; + + const gpre_req* request = action->act_request; + printa(column, "if (!%s) {", gen_name(s, request->req_eof, true)); +} + + +//____________________________________________________________ +// +// Substitute for a BASED ON clause. +// + +static void gen_based( const act* action, int column) +{ + USHORT datatype; + SLONG length = -1; + + align(column); + bas* based_on = (bas*) action->act_object; + const gpre_fld* field = based_on->bas_field; + + if (based_on->bas_flags & BAS_segment) + { + datatype = gpreGlob.sw_cstring ? dtype_cstring : dtype_text; + if (!(length = field->fld_seg_length)) + length = 256; + if (datatype == dtype_cstring) + length++; + } + else if (field->fld_array_info) + datatype = field->fld_array_info->ary_dtype; + else + datatype = field->fld_dtype; + + switch (datatype) + { + case dtype_short: + fprintf(gpreGlob.out_file, "short"); + break; + + case dtype_long: + fprintf(gpreGlob.out_file, DCL_LONG); + break; + + case dtype_quad: + fprintf(gpreGlob.out_file, DCL_QUAD); + break; + + // Begin date/time/timestamp + case dtype_sql_date: + fprintf(gpreGlob.out_file, "ISC_DATE"); + break; + + case dtype_sql_time: + fprintf(gpreGlob.out_file, "ISC_TIME"); + break; + + case dtype_timestamp: + fprintf(gpreGlob.out_file, "ISC_TIMESTAMP"); + break; + // End date/time/timestamp + + case dtype_int64: + fprintf(gpreGlob.out_file, "ISC_INT64"); + break; + + case dtype_blob: + fprintf(gpreGlob.out_file, "ISC_QUAD"); + break; + + case dtype_cstring: + case dtype_text: + case dtype_varying: + fprintf(gpreGlob.out_file, "char"); + break; + + case dtype_real: + fprintf(gpreGlob.out_file, "float"); + break; + + case dtype_double: + fprintf(gpreGlob.out_file, "double"); + break; + + default: + { + TEXT s[MAX_CURSOR_SIZE]; + sprintf(s, "datatype %d unknown\n", field->fld_dtype); + CPR_error(s); + return; + } + } + + // print the first variable, then precede the rest with commas + + column += INDENT; + + // Notice this variable was named first_flag, same than the global variable. + bool first = true; + + while (based_on->bas_variables) + { + const TEXT* variable = (TEXT*) MSC_pop(&based_on->bas_variables); + if (!first) + fprintf(gpreGlob.out_file, ","); + first = false; + align(column); + fprintf(gpreGlob.out_file, "%s", variable); + if (based_on->bas_flags & BAS_segment) + { + if (*variable != '*') + fprintf(gpreGlob.out_file, "[%"SLONGFORMAT"]", length); + } + else if (field->fld_array_info) + { + // Print out the dimension part of the declaration + + for (const dim* dimension = field->fld_array_info->ary_dimension; + dimension; dimension = dimension->dim_next) + { + fprintf(gpreGlob.out_file, " [%"SLONGFORMAT"]", dimension->dim_upper - dimension->dim_lower + 1); + } + + if (field->fld_array_info->ary_dtype <= dtype_varying && field->fld_length > 1) + { + fprintf(gpreGlob.out_file, " [%d]", field->fld_array->fld_length); + } + } + else + if (*variable != '*' && field->fld_dtype <= dtype_varying && + (field->fld_sub_type != 1 || field->fld_length > 1)) + { + // *??????? + //if (*variable != '*' && field->fld_dtype <= dtype_varying && + // field->fld_length > 1) + // + fprintf(gpreGlob.out_file, "[%d]", field->fld_length); + } + } + + fprintf(gpreGlob.out_file, "%s\n", based_on->bas_terminator); +} + + +//____________________________________________________________ +// +// Make a blob FOR loop. +// + +static void gen_blob_close( const act* action, USHORT column) +{ + const TEXT* pattern1 = "fb_%IFcancel%ELclose%EN_blob (%V1, &%BH);"; + + if (action->act_error) + begin(column); + + const blb* blob; + if (action->act_flags & ACT_sql) + { + column = gen_cursor_close(action, action->act_request, column); + blob = (blb*) action->act_request->req_blobs; + } + else + blob = (blb*) action->act_object; + + PAT args; + args.pat_blob = blob; + args.pat_vector1 = status_vector(action); + args.pat_condition = (action->act_type == ACT_blob_cancel); + PATTERN_expand(column, pattern1, &args); + + if (action->act_flags & ACT_sql) + { + endp(column); + column -= INDENT; + } + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// End a blob FOR loop. +// + +static void gen_blob_end( const act* action, USHORT column) +{ + PAT args; + TEXT s1[32]; + const TEXT* pattern1 = "}\n\ +&%BH->close(%V1);\n\ +}"; + + args.pat_blob = (const blb*) action->act_object; + if (action->act_error) + { + sprintf(s1, "%s2", global_status_name); + args.pat_vector1 = s1; + } + else + args.pat_vector1 = status_vector(0); + args.pat_condition = (action->act_type == ACT_blob_cancel); + PATTERN_expand(column, pattern1, &args); +} + + +//____________________________________________________________ +// +// Make a blob FOR loop. +// + +static void gen_blob_for( const act* action, USHORT column) +{ + PAT args; + const TEXT* pattern1 = "%IFif (%S1->isSuccess()) {\n\ +%ENwhile (1)\n\ + {"; + + gen_blob_open(action, column); + args.pat_condition = (action->act_error != NULL); + args.pat_string1 = global_status_name; + PATTERN_expand(column, pattern1, &args); + column += INDENT; + gen_get_segment(action, column); + printa(column, "if ((!%s->isSuccess()) && (%s->get()[1] != isc_segment)) break;", + global_status_name, global_status_name); +} + + +//____________________________________________________________ +// +// Generate the call to open (or create) a blob. +// + +static void gen_blob_open( const act* action, USHORT column) +{ + const TEXT* pattern1 = + "%BH = %DH->%IFcreate%ELopen%ENBlob (%V1, %RT, %FR, %N1, %I1);"; + const TEXT* pattern2 = + "%BH = %DH->%IFcreate%ELopen%ENBlob (%V1, %RT, %FR);"; + + if (gpreGlob.sw_auto && (action->act_flags & ACT_sql)) + { + t_start_auto(action, action->act_request, status_vector(action), column, true); + printa(column, "if (%s)", request_trans(action, action->act_request)); + column += INDENT; + } + + if ((action->act_error && (action->act_type != ACT_blob_for)) || (action->act_flags & ACT_sql)) + { + begin(column); + } + + TEXT s[MAX_REF_SIZE]; + const blb* blob; + const ref* reference; + if (action->act_flags & ACT_sql) + { + column = gen_cursor_open(action, action->act_request, column); + blob = (blb*) action->act_request->req_blobs; + reference = ((const open_cursor*) action->act_object)->opn_using; + gen_name(s, reference, true); + } + else + { + blob = (blb*) action->act_object; + reference = blob->blb_reference; + } + + PAT args; + args.pat_condition = (action->act_type == ACT_blob_create); // open or create blob + args.pat_vector1 = status_vector(action); // status vector + args.pat_database = blob->blb_request->req_database; // database handle + args.pat_request = blob->blb_request; // transaction handle + args.pat_blob = blob; // blob handle + args.pat_reference = reference; // blob identifier + args.pat_ident1 = blob->blb_bpb_ident; + + if ((action->act_flags & ACT_sql) && action->act_type == ACT_blob_open) + { + align(column); + fprintf(gpreGlob.out_file, "%s = %s;", s, reference->ref_value); + } + + if (args.pat_value1 = blob->blb_bpb_length) + PATTERN_expand(column, pattern1, &args); + else + PATTERN_expand(column, pattern2, &args); + + if (action->act_flags & ACT_sql) + { + endp(column); + column -= INDENT; + endp(column); + column -= INDENT; + endp(column); + if (gpreGlob.sw_auto) + column -= INDENT; + set_sqlcode(action, column); + if (action->act_type == ACT_blob_create) + { + printa(column, "if (!SQLCODE)"); + align(column + INDENT); + fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, s); + } + } + else if ((action->act_error && (action->act_type != ACT_blob_for))) + endp(column); +} + + +//____________________________________________________________ +// +// Callback routine for BLR pretty printer. +// + +static void gen_blr(void* /*user_arg*/, SSHORT /*offset*/, const char* string) +{ + char line[256]; + + int indent = 2 * INDENT; + const char* p = string; + while (*p == ' ') + { + p++; + indent++; + } + + // Limit indentation to 192 characters + + indent = MIN(indent, 192); + + bool first_line = true; + int length = strlen(p); + do { + const char* q; + if (length + indent > 255) + { + // if we did not find somewhere to break between the 200th and 256th + // character just print out 256 characters + + bool open_quote = false; + for (q = p; (q - p + indent) < 255; q++) + { + if ((q - p + indent) > 220 && *q == ',' && !open_quote) + break; + if (*q == '\'' && *(q - 1) != '\\') + open_quote = !open_quote; + } + ++q; + } + else { + q = p + strlen(p); + } + + // Replace all occurrences of gds__ (or gds__) with isc_ + + char* q1 = line; + for (const char* p1 = p; p1 < q;) + { + if ((*q1++ = *p1++) == 'g') + { + if (p1 < q && (*q1++ = *p1++) == 'd') + { + if (p1 < q && (*q1++ = *p1++) == 's') + { + if (p1 < q && (*q1++ = *p1++) == '_') + { + char d = 0; + if (p1 < q && ((d = *p1++) == '_' || d == '$')) + strncpy(q1 - 4, "isc", 3); + else if (d) + *q1++ = d; + } + } + } + } + } + *q1 = 0; + printa(indent, "%s", line); + length = length - (q - p); + p = q; + if (first_line) + { + indent = MIN(indent + INDENT, 192); + first_line = false; + } + } while (length > 0); +} + + +//____________________________________________________________ +// +// Zap all know handles. +// + +static void gen_clear_handles(int column) +{ + for (gpre_req* request = gpreGlob.requests; request; request = request->req_next) + { + if (!(request->req_flags & REQ_exp_hand)) + printa(column, "%s = 0;", request->req_handle); + } +} + + +//____________________________________________________________ +// +// Generate a symbol to ease compatibility with V3. +// +/* +static void gen_compatibility_symbol(const TEXT* symbol, + const TEXT* v4_prefix, const TEXT* trailer) +{ + const char* v3_prefix = isLangCpp(gpreGlob.sw_language) ? "gds_" : "gds__"; + // v3_prefix = gpreGlob.sw_language == lang_cxx ? "gds_" : "gds__"; + + fprintf(gpreGlob.out_file, "#define %s%s\t%s%s%s\n", v3_prefix, symbol, + v4_prefix, symbol, trailer); +} +*/ + +//____________________________________________________________ +// +// Generate text to compile a request. +// + +static void gen_compile( const act* action, int column) +{ + PAT args; + const TEXT* pattern1 = + "%RH = %DH->compileRequest(%V1, sizeof(%RI), %RI);"; + const TEXT* pattern2 = "if (!%RH%IF && %S1%EN)"; + + const gpre_req* request = action->act_request; + args.pat_request = request; + const gpre_dbb* db = request->req_database; + args.pat_database = db; + args.pat_vector1 = status_vector(action); + args.pat_string1 = request_trans(action, request); + args.pat_condition = (gpreGlob.sw_auto && (action->act_error || (action->act_flags & ACT_sql))); + + if (gpreGlob.sw_auto) + t_start_auto(action, request, status_vector(action), column, true); + + PATTERN_expand((USHORT) column, pattern2, &args); + + args.pat_condition = !(request->req_flags & REQ_exp_hand); + args.pat_value1 = request->req_length; + PATTERN_expand((USHORT) (column + INDENT), pattern1, &args); + + // If blobs are present, zero out all of the blob handles. After this + // point, the handles are the user's responsibility + + const blb* blob = request->req_blobs; + if (blob) + { + fprintf(gpreGlob.out_file, "\n"); + align(column - INDENT); + for (; blob; blob = blob->blb_next) + fprintf(gpreGlob.out_file, "fb_%d = ", blob->blb_ident); + fprintf(gpreGlob.out_file, "0;"); + } +} + + +//____________________________________________________________ +// +// Generate a call to create a database. +// + +static void gen_create_database( const act* action, int column) +{ + const TEXT* pattern1 = + "%DH = fbProvider->createDatabase (%V1, \"%DF\", %IF%S1, %S2%EL0, NULL%EN);"; + TEXT s1[32], s2[32], trname[32]; + + const gpre_req* request = ((const mdbb*) action->act_object)->mdbb_dpb_request; + const gpre_dbb* db = request->req_database; + + sprintf(s1, "fb_%dl", request->req_ident); + sprintf(trname, "fb_%dt", request->req_ident); + + if (request->req_flags & REQ_extend_dpb) + { + sprintf(s2, "fb_%dp", request->req_ident); + if (request->req_length) + printa(column, "%s = fb_%d;", s2, request->req_ident); + else + printa(column, "%s = (char*) 0;", s2); + + printa(column, + "isc_expand_dpb (&%s, &%s, isc_dpb_user_name, %s, isc_dpb_password, %s, isc_dpb_sql_role_name, %s, isc_dpb_lc_messages, %s, isc_dpb_lc_ctype, %s, 0);", + s2, s1, + db->dbb_r_user ? db->dbb_r_user : "(char*) 0", + db->dbb_r_password ? db->dbb_r_password : "(char*) 0", + db->dbb_r_sql_role ? db->dbb_r_sql_role : "(char*) 0", + db->dbb_r_lc_messages ? db->dbb_r_lc_messages : "(char*) 0", + db->dbb_r_lc_ctype ? db->dbb_r_lc_ctype : "(char*) 0"); + } + else + sprintf(s2, "fb_%d", request->req_ident); + + PAT args; + args.pat_vector1 = status_vector(action); + args.pat_request = request; + args.pat_database = db; + args.pat_value1 = strlen(db->dbb_filename); + args.pat_condition = (request->req_length || (request->req_flags & REQ_extend_dpb)); + args.pat_string1 = s1; + args.pat_string2 = s2; + + PATTERN_expand((USHORT) column, pattern1, &args); + + // if the dpb was extended, free it here + + if (request->req_flags & REQ_extend_dpb) + { + if (request->req_length) + printa(column, "if (%s != fb_%d)", s2, request->req_ident); + printa(column + (request->req_length ? INDENT : 0), "isc_free ((char*) %s);", s2); + + // reset the length of the dpb + + printa(column, "%s = %d;", s1, request->req_length); + } + + request = action->act_request; + printa(column, "if (%s->isSuccess())", global_status_name); + column += INDENT; + begin(column); + printa(column, + "%s = %s->startTransaction (%s, 0, NULL);", + trname, db->dbb_name->sym_string, status_vector(action)); + printa(column, "if (%s)", trname); + column += INDENT; + align(column); + fprintf(gpreGlob.out_file, "%s->executeDyn(%s, %s, %d, fb_%d);", + request->req_database->dbb_name->sym_string, status_vector(action), + trname, request->req_length, request->req_ident); + column -= INDENT; + printa(column, "if (%s->isSuccess())", global_status_name); + printa(column + INDENT, "%s->commit(%s);", + trname, status_vector(action)); + printa(column, "if (!%s->isSuccess())", global_status_name); + printa(column + INDENT, "%s->rollback(%s);", + trname, status_vector(NULL)); + set_sqlcode(action, column); + endp(column); + printa(column - INDENT, "else"); + set_sqlcode(action, column); + column -= INDENT; +} + + +//____________________________________________________________ +// +// Generate substitution text for END_STREAM. +// + +static int gen_cursor_close( const act* action, const gpre_req* request, int column) +{ + PAT args; + ObjectNotImplemented(); + const TEXT* pattern1 = "if (%RIs && !isc_dsql_free_statement (%V1, &%RIs, %L1))"; + + args.pat_request = request; + args.pat_vector1 = status_vector(action); + args.pat_long1 = 1; + + PATTERN_expand((USHORT) column, pattern1, &args); + column += INDENT; + begin(column); + + return column; +} + + +//____________________________________________________________ +// +// Generate text to initialize a cursor. +// + +static void gen_cursor_init( const act* action, int column) +{ + + // If blobs are present, zero out all of the blob handles. After this + // point, the handles are the user's responsibility + + if (action->act_request->req_flags & (REQ_sql_blob_open | REQ_sql_blob_create)) + { + printa(column, "fb_%d = 0;", action->act_request->req_blobs->blb_ident); + } +} + + +//____________________________________________________________ +// +// Generate text to open an embedded SQL cursor. +// + +static int gen_cursor_open( const act* action, const gpre_req* request, int column) +{ + PAT args; + TEXT s[MAX_CURSOR_SIZE]; + ObjectNotImplemented(); + const TEXT* pattern1 = "if (!%RIs && %RH%IF && %DH%EN)"; + const TEXT* pattern2 = "if (!%RIs%IF && %DH%EN)"; + const TEXT* pattern3 = "isc_dsql_alloc_statement2 (%V1, &%DH, &%RIs);"; + const TEXT* pattern4 = "if (%RIs%IF && %S3%EN)"; + const TEXT* pattern5 = "if (!isc_dsql_set_cursor_name (%V1, &%RIs, %S1, 0) &&"; + const TEXT* pattern6 = "!isc_dsql_execute_m (%V1, &%S3, &%RIs, 0, %S2, %N2, 0, %S2))"; + + args.pat_request = request; + args.pat_database = request->req_database; + args.pat_vector1 = status_vector(action); + args.pat_condition = gpreGlob.sw_auto; + args.pat_string1 = make_name(s, ((open_cursor*) action->act_object)->opn_cursor); + args.pat_string2 = NULL_STRING; + args.pat_string3 = request_trans(action, request); + args.pat_value2 = -1; + + PATTERN_expand((USHORT) column, (action->act_type == ACT_open) ? pattern1 : pattern2, &args); + PATTERN_expand((USHORT) (column + INDENT), pattern3, &args); + PATTERN_expand((USHORT) column, pattern4, &args); + column += INDENT; + begin(column); + PATTERN_expand((USHORT) column, pattern5, &args); + column += INDENT; + PATTERN_expand((USHORT) column, pattern6, &args); + begin(column); + + return column; +} + + +//____________________________________________________________ +// +// Generate insertion text for the database statement. +// + +static void gen_database(int column) +{ + if (global_first_flag) + return; + + global_first_flag = true; + + fprintf(gpreGlob.out_file, "\n/**** GDS Preprocessor Definitions ****/\n"); + fprintf(gpreGlob.out_file, "#ifndef JRD_IBASE_H\n#include %s\n#endif\n", GDS_INCLUDE); + fprintf(gpreGlob.out_file, "#include \n"); + + fprintf(gpreGlob.out_file, "#define CAST_CONST_MSG(A) (reinterpret_cast(A))\n"); + fprintf(gpreGlob.out_file, "#define CAST_MSG(A) (reinterpret_cast(A))\n"); + + printa(column, "static %sISC_QUAD", CONST_STR); + printa(column + INDENT, "fbBlobNull = {0, 0};\t/* initializer for blobs */"); + + const TEXT* scope = ""; + + bool all_static = true; + bool all_extern = true; + + const gpre_dbb* db; + for (db = gpreGlob.isc_databases; db; db = db->dbb_next) + { + all_static = all_static && (db->dbb_scope == DBB_STATIC); + all_extern = all_extern && (db->dbb_scope == DBB_EXTERN); + if (db->dbb_scope == DBB_STATIC) + scope = "static "; + else if (db->dbb_scope == DBB_EXTERN) + scope = "extern "; + printa(column, "%sFirebird::IAttachment*", scope); + if (!all_extern) + printa(column + INDENT, "%s = 0;\t\t/* database handle */\n", db->dbb_name->sym_string); + else + printa(column + INDENT, "%s;\t\t/* database handle */\n", db->dbb_name->sym_string); + } + + if (all_static) + scope = "static "; + else if (all_extern) + scope = "extern "; + + printa(column, "%sFirebird::ITransaction*", scope); + if (!all_extern) + printa(column + INDENT, "%s = 0;\t\t/* default transaction handle */", + gpreGlob.transaction_name); + else + printa(column + INDENT, "%s;\t\t/* default transaction handle */", + gpreGlob.transaction_name); + + printa(column, "%sFirebird::IMaster* fbMaster%s;\t\t/* master interface */", + scope, all_extern ? "" : " = fb_get_master_interface()"); + printa(column, "%sFirebird::IProvider* fbProvider%s;\t\t/* provider interface */", + scope, all_extern ? "" : " = fbMaster->getDispatcher()"); + + printa(column, "%sFirebird::IStatus* %s%s;\t/* status vector */", + scope, global_status_name, all_extern ? "" : " = fbMaster->getStatus();"); + printa(column, "%sFirebird::IStatus* %s2%s;\t/* status vector */", + scope, global_status_name, all_extern ? "" : " = fbMaster->getStatus();"); + + for (db = gpreGlob.isc_databases; db; db = db->dbb_next) + for (const tpb* tpb_iterator = db->dbb_tpbs; tpb_iterator; + tpb_iterator = tpb_iterator->tpb_dbb_next) + { + gen_tpb(tpb_iterator, column); + } + + // generate event parameter block for each event in module + + SSHORT max_count = 0; + for (gpre_lls* stack_ptr = gpreGlob.events; stack_ptr; stack_ptr = stack_ptr->lls_next) + { + SSHORT count = gen_event_block((act*) stack_ptr->lls_object); + max_count = MAX(count, max_count); + } + + if (max_count) + printa(column, "%s%s isc_events [%d];\t/* event vector */", scope, DCL_LONG, max_count); + + for (gpre_req* request = gpreGlob.requests; request; request = request->req_next) + { + gen_request(request); + + // Array declarations + + if (gpre_port* port = request->req_primary) + for (ref* reference = port->por_references; reference; reference = reference->ref_next) + { + if (reference->ref_flags & REF_fetch_array) + make_array_declaration(reference); + } + } + + fprintf(gpreGlob.out_file, "\n\n"); +/* + gen_compatibility_symbol("blob_null", "isc_", "\t// compatibility symbols"); + //gen_compatibility_symbol ("database", "isc_", ""); + //gen_compatibility_symbol ("trans", "isc_", ""); + gen_compatibility_symbol("status", "isc_", ""); + gen_compatibility_symbol("status2", "isc_", ""); + gen_compatibility_symbol("array_length", "isc_", ""); + if (max_count) + gen_compatibility_symbol("events", "isc_", ""); + gen_compatibility_symbol("count", "isc_", ""); + gen_compatibility_symbol("slack", "isc_", ""); + gen_compatibility_symbol("utility", "isc_", "\t// end of compatibility symbols"); + */ +} + + +//____________________________________________________________ +// +// Generate a call to update metadata. +// + +static void gen_ddl( const act* action, int column) +{ + // Set up command type for call to RDB$DDL + + const gpre_req* request = action->act_request; + + if (gpreGlob.sw_auto) + { + t_start_auto(action, 0, status_vector(action), column, true); + printa(column, "if (%s)", gpreGlob.transaction_name); + column += INDENT; + } + + align(column); + fprintf(gpreGlob.out_file, "%s->executeDyn(%s, %s, %d, fb_%d);", + request->req_database->dbb_name->sym_string, + status_vector(action), + gpreGlob.transaction_name, request->req_length, request->req_ident); + + if (gpreGlob.sw_auto) + { + column -= INDENT; + printa(column, "if (%s->isSuccess())", global_status_name); + printa(column + INDENT, "%s->commit(%s);", + gpreGlob.transaction_name, status_vector(action)); + printa(column, "if (!%s->isSuccess())", global_status_name); + printa(column + INDENT, "%s->rollback(%s);", + gpreGlob.transaction_name, status_vector(NULL)); + } + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a call to drop a database. +// + +static void gen_drop_database( const act* action, int column) +{ + ObjectNotImplemented(); + const gpre_dbb* db = (gpre_dbb*) action->act_object; + align(column); + + fprintf(gpreGlob.out_file, "isc_drop_database (%s, %"SIZEFORMAT", \"%s\", rdb$k_db_type_gds);", + status_vector(action), + strlen(db->dbb_filename), db->dbb_filename); + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_close( const act* action, int column) +{ + TEXT s[MAX_CURSOR_SIZE]; + + ObjectNotImplemented(); + const dyn* statement = (dyn*) action->act_object; + printa(column, "isc_embed_dsql_close (%s, %s);", + global_status_name, make_name(s, statement->dyn_cursor_name)); + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_declare( const act* action, int column) +{ + TEXT s1[MAX_CURSOR_SIZE], s2[MAX_CURSOR_SIZE]; + + ObjectNotImplemented(); + const dyn* statement = (dyn*) action->act_object; + printa(column, "isc_embed_dsql_declare (%s, %s, %s);", + global_status_name, + make_name(s1, statement->dyn_statement_name), + make_name(s2, statement->dyn_cursor_name)); + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_describe(const act* action, int column, bool bind_flag) +{ + TEXT s[MAX_CURSOR_SIZE]; + + ObjectNotImplemented(); + const dyn* statement = (dyn*) action->act_object; + printa(column, "isc_embed_dsql_describe%s (%s, %s, %d, %s);", + bind_flag ? "_bind" : "", + global_status_name, + make_name(s, statement->dyn_statement_name), + gpreGlob.sw_sql_dialect, statement->dyn_sqlda); + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_execute( const act* action, int column) +{ + TEXT s[MAX_CURSOR_SIZE]; + gpre_req* request; + gpre_req req_const; + + ObjectNotImplemented(); + dyn* statement = (dyn*) action->act_object; + const TEXT* transaction; + if (statement->dyn_trans) + { + transaction = statement->dyn_trans; + request = &req_const; + request->req_trans = transaction; + } + else + { + transaction = gpreGlob.transaction_name; + request = NULL; + } + + if (gpreGlob.sw_auto) + { + t_start_auto(action, request, status_vector(action), column, true); + printa(column, "if (%s)", transaction); + column += INDENT; + } + + if (statement->dyn_sqlda2) + printa(column, "isc_embed_dsql_execute2 (%s, &%s, %s, %d, %s, %s);", + global_status_name, + transaction, + make_name(s, statement->dyn_statement_name), + gpreGlob.sw_sql_dialect, + statement->dyn_sqlda ? statement->dyn_sqlda : NULL_STRING, + statement->dyn_sqlda2); + else + printa(column, "isc_embed_dsql_execute (%s, &%s, %s, %d, %s);", + global_status_name, + transaction, + make_name(s, statement->dyn_statement_name), + gpreGlob.sw_sql_dialect, + statement->dyn_sqlda ? statement->dyn_sqlda : NULL_STRING); + + if (gpreGlob.sw_auto) + column -= INDENT; + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_fetch( const act* action, int column) +{ + TEXT s[MAX_CURSOR_SIZE]; + + ObjectNotImplemented(); + const dyn* statement = (dyn*) action->act_object; + printa(column, "SQLCODE = isc_embed_dsql_fetch (%s, %s, %d, %s);", + global_status_name, make_name(s, statement->dyn_cursor_name), + gpreGlob.sw_sql_dialect, + statement->dyn_sqlda ? statement->dyn_sqlda : NULL_SQLDA); + + printa(column, "if (SQLCODE != 100) SQLCODE = isc_sqlcode (%s);", global_status_name); +} + + +//____________________________________________________________ +// +// Generate code for an EXECUTE IMMEDIATE dynamic SQL statement. +// + +static void gen_dyn_immediate( const act* action, int column) +{ + gpre_req* request; + gpre_req req_const; + + ObjectNotImplemented(); + const dyn* statement = (dyn*) action->act_object; + const gpre_dbb* database = statement->dyn_database; + const TEXT* transaction; + if (statement->dyn_trans) + { + transaction = statement->dyn_trans; + request = &req_const; + request->req_trans = transaction; + } + else + { + transaction = gpreGlob.transaction_name; + request = NULL; + } + + if (gpreGlob.sw_auto) + { + t_start_auto(action, request, status_vector(action), column, true); + printa(column, "if (%s)", transaction); + column += INDENT; + } + + printa(column, + statement->dyn_sqlda2 ? + "isc_embed_dsql_execute_immed2 (%s, &%s, &%s, 0, %s, %d, %s, %s);" : + "isc_embed_dsql_execute_immed (%s, &%s, &%s, 0, %s, %d, %s%s);", + global_status_name, database->dbb_name->sym_string, transaction, + statement->dyn_string, gpreGlob.sw_sql_dialect, + statement->dyn_sqlda ? statement->dyn_sqlda : NULL_SQLDA, + statement->dyn_sqlda2 ? statement->dyn_sqlda2 : ""); + + if (gpreGlob.sw_auto) + column -= INDENT; + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_insert( const act* action, int column) +{ + TEXT s[MAX_CURSOR_SIZE]; + + ObjectNotImplemented(); + const dyn* statement = (dyn*) action->act_object; + printa(column, "isc_embed_dsql_insert (%s, %s, %d, %s);", + global_status_name, + make_name(s, statement->dyn_cursor_name), + gpreGlob.sw_sql_dialect, + statement->dyn_sqlda ? statement->dyn_sqlda : NULL_SQLDA); + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_open( const act* action, int column) +{ + gpre_req* request; + gpre_req req_const; + + ObjectNotImplemented(); + dyn* statement = (dyn*) action->act_object; + const TEXT* transaction; + if (statement->dyn_trans) + { + transaction = statement->dyn_trans; + request = &req_const; + request->req_trans = transaction; + } + else + { + transaction = gpreGlob.transaction_name; + request = NULL; + } + + if (gpreGlob.sw_auto) + { + t_start_auto(action, request, status_vector(action), column, true); + printa(column, "if (%s)", transaction); + column += INDENT; + } + + TEXT s[MAX_CURSOR_SIZE]; + make_name(s, statement->dyn_cursor_name); + + printa(column, + statement->dyn_sqlda2 ? + "isc_embed_dsql_open2 (%s, &%s, %s, %d, %s, %s);" : + "isc_embed_dsql_open (%s, &%s, %s, %d, %s%s);", + global_status_name, + transaction, + s, + gpreGlob.sw_sql_dialect, + statement->dyn_sqlda ? statement->dyn_sqlda : NULL_SQLDA, + statement->dyn_sqlda2 ? statement->dyn_sqlda2 : ""); + + if (gpreGlob.sw_auto) + column -= INDENT; + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a dynamic SQL statement. +// + +static void gen_dyn_prepare( const act* action, int column) +{ + gpre_req* request; + gpre_req req_const; + + ObjectNotImplemented(); + dyn* statement = (dyn*) action->act_object; + const TEXT* transaction; + if (statement->dyn_trans) + { + transaction = statement->dyn_trans; + request = &req_const; + request->req_trans = transaction; + } + else + { + transaction = gpreGlob.transaction_name; + request = NULL; + } + + if (gpreGlob.sw_auto) + { + t_start_auto(action, request, status_vector(action), column, true); + printa(column, "if (%s)", transaction); + column += INDENT; + } + + TEXT s[MAX_CURSOR_SIZE]; + const gpre_dbb* database = statement->dyn_database; + printa(column, "isc_embed_dsql_prepare (%s, &%s, &%s, %s, 0, %s, %d, %s);", + global_status_name, database->dbb_name->sym_string, transaction, + make_name(s, statement->dyn_statement_name), statement->dyn_string, + gpreGlob.sw_sql_dialect, + statement->dyn_sqlda ? statement->dyn_sqlda : NULL_SQLDA); + + if (gpreGlob.sw_auto) + column -= INDENT; + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate substitution text for END_MODIFY. +// +// Trickier because a fixed subtype single character +// field is a single character, not a pointer to a +// single character. +// + +static void gen_emodify( const act* action, int column) +{ + TEXT s1[MAX_REF_SIZE], s2[MAX_REF_SIZE]; + + const upd* modify = (upd*) action->act_object; + + for (const ref* reference = modify->upd_port->por_references; reference; + reference = reference->ref_next) + { + const ref* source = reference->ref_source; + if (!source) + continue; + const gpre_fld* field = reference->ref_field; + align(column); + gen_name(s1, source, true); + gen_name(s2, reference, true); + if (field->fld_dtype > dtype_cstring || (field->fld_sub_type == 1 && field->fld_length == 1)) + { + fprintf(gpreGlob.out_file, "%s = %s;", s2, s1); + } + else if (gpreGlob.sw_cstring && !field->fld_sub_type) + fprintf(gpreGlob.out_file, isLangCpp(gpreGlob.sw_language) ? + "isc_vtov ((const char*) %s, (char*) %s, %d);" : + "isc_vtov ((char*) %s, (char*) %s, %d);", + s1, s2, field->fld_length); + else + fprintf(gpreGlob.out_file, "isc_ftof (%s, %d, %s, %d);", + s1, field->fld_length, s2, field->fld_length); + if (field->fld_array_info) + gen_get_or_put_slice(action, reference, false, column); + } + + gen_send(action, modify->upd_port, column); + +} + + +//____________________________________________________________ +// +// Generate substitution text for END_STORE. +// + +static void gen_estore( const act* action, int column) +{ + const gpre_req* request = action->act_request; + + // if we did a store ... returning_values aka store2 + // just wrap up pending error and return + + if (request->req_type == REQ_store2) + { + if (action->act_error) + endp(column); + return; + } + + if (action->act_error) + column += INDENT; + + align(column); + gen_start(action, request->req_primary, column, true); + + if (action->act_error) + endp(column); +} + + +//____________________________________________________________ +// +// Generate definitions associated with a single request. +// + +static void gen_endfor( const act* action, int column) +{ + const gpre_req* request = action->act_request; + column += INDENT; + + if (request->req_sync) + gen_send(action, request->req_sync, column); + + endp(column); + + if (action->act_error || (action->act_flags & ACT_sql)) + endp(column); +} + + +//____________________________________________________________ +// +// Generate substitution text for ERASE. +// + +static void gen_erase( const act* action, int column) +{ + if (action->act_error || (action->act_flags & ACT_sql)) + begin(column); + + const upd* erase = (upd*) action->act_object; + gen_send(action, erase->upd_port, column); + + if (action->act_flags & ACT_sql) + endp(column); +} + + +//____________________________________________________________ +// +// Generate event parameter blocks for use +// with a particular call to isc_event_wait. +// + +static SSHORT gen_event_block(act* action) +{ + ObjectNotImplemented(); + gpre_nod* init = (gpre_nod*) action->act_object; + //gpre_sym* event_name = (gpre_sym*) init->nod_arg[0]; + + int ident = CMP_next_ident(); + init->nod_arg[2] = (gpre_nod*)(IPTR)ident; + + printa(0, "static %sunsigned char\n *fb_%da, *fb_%db;", CONST_STR, ident, ident); + printa(0, "static unsigned\n fb_%dl;", ident); + + const gpre_nod* list = init->nod_arg[1]; + return list->nod_count; +} + + +//____________________________________________________________ +// +// Generate substitution text for EVENT_INIT. +// + +static void gen_event_init( const act* action, int column) +{ + ObjectNotImplemented(); + const TEXT* pattern1 = "fb_%L1l = isc_event_block (&fb_%L1a, &fb_%L1b, (short) %N2"; + const TEXT* pattern2 = "isc_wait_for_event (%V1, &%DH, fb_%L1l, fb_%L1a, fb_%L1b);"; + const TEXT* pattern3 = "isc_event_counts (fb_events, fb_%L1l, fb_%L1a, fb_%L1b);"; + + if (action->act_error) + begin(column); + begin(column); + + const gpre_nod* init = (gpre_nod*) action->act_object; + const gpre_nod* event_list = init->nod_arg[1]; + + PAT args; + args.pat_database = (gpre_dbb*) init->nod_arg[3]; + args.pat_vector1 = status_vector(action); + args.pat_long1 = (IPTR) init->nod_arg[2]; + args.pat_value2 = (int) event_list->nod_count; + + // generate call to dynamically generate event blocks + + PATTERN_expand((USHORT) column, pattern1, &args); + + TEXT variable[MAX_REF_SIZE]; + const gpre_nod* const* ptr = event_list->nod_arg; + for (const gpre_nod* const* const end = ptr + event_list->nod_count; ptr < end; ptr++) + { + const gpre_nod* node = *ptr; + if (node->nod_type == nod_field) + { + const ref* reference = (ref*) node->nod_arg[0]; + gen_name(variable, reference, true); + printb(", %s", variable); + } + else + printb(", %s", (TEXT* ) node->nod_arg[0]); + } + + printb(");"); + + // generate actual call to event_wait + + PATTERN_expand((USHORT) column, pattern2, &args); + + // get change in event counts, copying event parameter block for reuse + + PATTERN_expand((USHORT) column, pattern3, &args); + + if (action->act_error) + endp(column); + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate substitution text for EVENT_WAIT. +// + +static void gen_event_wait( const act* action, int column) +{ + ObjectNotImplemented(); + TEXT s[64]; + const TEXT* pattern1 = "isc_wait_for_event (%V1, &%DH, fb_%L1l, fb_%L1a, fb_%L1b);"; + const TEXT* pattern2 = "isc_event_counts (isc_events, fb_%L1l, fb_%L1a, fb_%L1b);"; + + if (action->act_error) + begin(column); + begin(column); + + const gpre_sym* event_name = (gpre_sym*) action->act_object; + + // go through the stack of gpreGlob.events, checking to see if the + // event has been initialized and getting the event identifier + + const gpre_dbb* database = NULL; + int ident = -1; + for (const gpre_lls* stack_ptr = gpreGlob.events; stack_ptr; stack_ptr = stack_ptr->lls_next) + { + const act* event_action = (const act*) stack_ptr->lls_object; + const gpre_nod* event_init = (gpre_nod*) event_action->act_object; + const gpre_sym* stack_name = (gpre_sym*) event_init->nod_arg[0]; + if (!strcmp(event_name->sym_string, stack_name->sym_string)) + { + ident = (IPTR) event_init->nod_arg[2]; + database = (gpre_dbb*) event_init->nod_arg[3]; + } + } + + if (ident < 0) + { + sprintf(s, "event handle \"%s\" not found", event_name->sym_string); + CPR_error(s); + return; + } + + PAT args; + args.pat_database = database; + args.pat_vector1 = status_vector(action); + args.pat_long1 = ident; + + // generate calls to wait on the event and to fill out the gpreGlob.events array + + PATTERN_expand((USHORT) column, pattern1, &args); + PATTERN_expand((USHORT) column, pattern2, &args); + + if (action->act_error) + endp(column); + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate replacement text for the SQL FETCH statement. The +// epilog FETCH statement is handled by GEN_S_FETCH (generate +// stream fetch). +// + +static void gen_fetch( const act* action, int column) +{ + gpre_req* request = action->act_request; + + if (request->req_sync) + { + gen_send(action, request->req_sync, column); + printa(column, "if (!SQLCODE)"); + column += INDENT; + begin(column); + } + + TEXT s[MAX_REF_SIZE]; + gen_receive(action, column, request->req_primary); + printa(column, "if (!SQLCODE)"); + column += INDENT; + printa(column, "if (%s)", gen_name(s, request->req_eof, true)); + column += INDENT; + begin(column); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + if (var_list) + for (int i = 0; i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) (var_list->nod_arg[i]), column); + } + + endp(column); + printa(column - INDENT, "else"); + printa(column, "SQLCODE = 100;"); + + if (request->req_sync) + { + column -= INDENT; + endp(column); + } +} + + +//____________________________________________________________ +// +// Generate substitution text for FINISH +// + +static void gen_finish( const act* action, int column) +{ + PAT args; + const TEXT* pattern1 = "if (%S2)\n %S2->%S1(%V1);"; + + args.pat_vector1 = status_vector(action); + args.pat_string2 = gpreGlob.transaction_name; + + if (gpreGlob.sw_auto || ((action->act_flags & ACT_sql) && action->act_type != ACT_disconnect)) + { + args.pat_string1 = ((action->act_type != ACT_rfinish) ? "commit" : "rollback"); + PATTERN_expand((USHORT) column, pattern1, &args); + } + + const gpre_dbb* db = NULL; + + // the user supplied one or more db_handles + + for (const rdy* ready = (rdy*) action->act_object; ready; ready = ready->rdy_next) + { + db = ready->rdy_database; + printa(column, "%s->detach(%s);", + db->dbb_name->sym_string, status_vector(action)); + } + // no hanbdles, so we finish all known databases + + if (!db) + { + for (db = gpreGlob.isc_databases; db; db = db->dbb_next) + { + if ((action->act_error || (action->act_flags & ACT_sql)) && db != gpreGlob.isc_databases) + { + printa(column, "if (%s && %s->isSuccess()) ", db->dbb_name->sym_string, global_status_name); + } + else + printa(column, "if (%s)", db->dbb_name->sym_string); + printa(column + INDENT, "%s->detach(%s);", + db->dbb_name->sym_string, status_vector(action)); + } + } + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate substitution text for FOR statement. +// + +static void gen_for( const act* action, int column) +{ + gen_s_start(action, column); + const gpre_req* request = action->act_request; + + if (action->act_error || (action->act_flags & ACT_sql)) + printa(column, "if (%s->isSuccess()) {", global_status_name); + + printa(column, "while (1)"); + column += INDENT; + begin(column); + gen_receive(action, column, request->req_primary); + + TEXT s[MAX_REF_SIZE]; + if (action->act_error || (action->act_flags & ACT_sql)) + printa(column, "if (!%s || (!%s->isSuccess())) break;", + gen_name(s, request->req_eof, true), global_status_name); + else + printa(column, "if (!%s) break;", gen_name(s, request->req_eof, true)); + + const gpre_port* port = action->act_request->req_primary; + if (port) + for (const ref* reference = port->por_references; reference; reference = reference->ref_next) + { + if (reference->ref_field->fld_array_info) + gen_get_or_put_slice(action, reference, true, column); + } + +} + + +//____________________________________________________________ +// +// Generate a function for free standing ANY or statistical. +// + +static void gen_function( const act* function, int column) +{ + const ref* reference; + + const act* action = (const act*) function->act_object; + + if (action->act_type != ACT_any) + { + CPR_error("can't generate function"); + return; + } + + const gpre_req* request = action->act_request; + + fprintf(gpreGlob.out_file, "static %s_r (Firebird::IRequest* request, Firebird::ITransaction* transaction ", request->req_handle); + + TEXT s[MAX_REF_SIZE]; + gpre_port* port = request->req_vport; + if (port) + for (reference = port->por_references; reference; reference = reference->ref_next) + { + fprintf(gpreGlob.out_file, ", %s", gen_name(s, reference->ref_source, true)); + } + + fprintf(gpreGlob.out_file, + ")\n"); + + if (port) + for (reference = port->por_references; reference; reference = reference->ref_next) + { + const TEXT* dtype; + gpre_fld* field = reference->ref_field; + switch (field->fld_dtype) + { + case dtype_short: + dtype = "short"; + break; + + case dtype_long: + dtype = DCL_LONG; + break; + + case dtype_cstring: + case dtype_text: + dtype = "char*"; + break; + + case dtype_quad: + dtype = DCL_QUAD; + break; + + // Begin date/time/timestamp + case dtype_sql_date: + dtype = "ISC_DATE"; + break; + + case dtype_sql_time: + dtype = "ISC_TIME"; + break; + + case dtype_timestamp: + dtype = "ISC_TIMESTAMP"; + break; + // End date/time/timestamp + + case dtype_int64: + dtype = "ISC_INT64"; + break; + + case dtype_blob: + dtype = "ISC_QUAD"; + break; + + case dtype_real: + dtype = "float"; + break; + + case dtype_double: + dtype = "double"; + break; + + default: + CPR_error("gen_function: unsupported datatype"); + return; + } + fprintf(gpreGlob.out_file, " %s\t%s;\n", dtype, + gen_name(s, reference->ref_source, true)); + } + + fprintf(gpreGlob.out_file, "{\n"); + for (port = request->req_ports; port; port = port->por_next) + make_port(port, column); + + fprintf(gpreGlob.out_file, "\n\n"); + gen_s_start(action, 0); + gen_receive(action, column, request->req_primary); + + for (port = request->req_ports; port; port = port->por_next) + for (reference = port->por_references; reference; reference = reference->ref_next) + { + if (reference->ref_field->fld_array_info) + gen_get_or_put_slice(action, reference, true, column); + } + + port = request->req_primary; + fprintf(gpreGlob.out_file, "\nreturn %s;\n}\n", gen_name(s, port->por_references, true)); +} + + +//____________________________________________________________ +// +// Generate a call to isc_get_slice +// or isc_put_slice for an array. +// + +static void gen_get_or_put_slice(const act* action, const ref* reference, bool get, int column) +{ + ObjectNotImplemented(); + const TEXT* pattern1 = + "isc_get_slice (%V1, &%DH, &%RT, &%S2, (short) %N1, (char*) %S3, 0, (%S6*) 0, (%S6) %L1, %S5, &isc_array_length);"; + const TEXT* pattern2 = + "isc_put_slice (%V1, &%DH, &%RT, &%S2, (short) %N1, (char*) %S3, 0, (%S6*) 0, (%S6) %L1, (void*) %S5);"; + + if (!(reference->ref_flags & REF_fetch_array)) + return; + + TEXT s1[MAX_REF_SIZE], s2[MAX_REF_SIZE]; + PAT args; + args.pat_request = action->act_request; + args.pat_condition = get; // get or put slice + args.pat_vector1 = status_vector(action); // status vector + args.pat_database = action->act_request->req_database; // database handle + gen_name(s1, reference, true); // blob handle + args.pat_string2 = s1; + args.pat_value1 = reference->ref_sdl_length; // slice description length + sprintf(s2, "fb_%d", reference->ref_sdl_ident); // slice description + args.pat_string3 = s2; + + args.pat_long1 = reference->ref_field->fld_array_info->ary_size; + // slice size + + TEXT s4[MAX_REF_SIZE]; + if (action->act_flags & ACT_sql) + args.pat_string5 = reference->ref_value; + else + { + sprintf(s4, "fb_%d", reference->ref_field->fld_array_info->ary_ident); + args.pat_string5 = s4; // array name + } + + args.pat_string6 = DCL_LONG; + + PATTERN_expand((USHORT) column, get ? pattern1 : pattern2, &args); + + set_sqlcode(action, column); + if (action->act_flags & ACT_sql) + gen_whenever(action->act_whenever, column); +} + + +//____________________________________________________________ +// +// Generate the code to do a get segment. +// + +static void gen_get_segment( const act* action, int column) +{ + ObjectNotImplemented(); + const TEXT* pattern1 = + "%IF%S1 [1] = %ENisc_get_segment (%V1, &%BH, &%I1, (short) sizeof(%I2), %I2);"; + + if (action->act_error && (action->act_type != ACT_blob_for)) + begin(column); + + const blb* blob; + if (action->act_flags & ACT_sql) + blob = (blb*) action->act_request->req_blobs; + else + blob = (blb*) action->act_object; + + PAT args; + args.pat_blob = blob; + args.pat_vector1 = status_vector(action); + args.pat_condition = !(action->act_error || (action->act_flags & ACT_sql)); + args.pat_ident1 = blob->blb_len_ident; + args.pat_ident2 = blob->blb_buff_ident; + args.pat_string1 = global_status_name; + PATTERN_expand((USHORT) column, pattern1, &args); + + if (action->act_flags & ACT_sql) + { + const ref* into = action->act_object; + set_sqlcode(action, column); + printa(column, "if (!SQLCODE || SQLCODE == 101)"); + column += INDENT; + begin(column); + align(column); + fprintf(gpreGlob.out_file, "isc_ftof (fb_%d, fb_%d, %s, fb_%d);", + blob->blb_buff_ident, blob->blb_len_ident, + into->ref_value, blob->blb_len_ident); + if (into->ref_null_value) + { + align(column); + fprintf(gpreGlob.out_file, "%s = fb_%d;", into->ref_null_value, blob->blb_len_ident); + } + endp(column); + column -= INDENT; + } +} + + +//____________________________________________________________ +// +// Generate text to compile and start a SQL command +// + +static void gen_loop( const act* action, int column) +{ + TEXT name[MAX_REF_SIZE]; + + gen_s_start(action, column); + const gpre_req* request = action->act_request; + const gpre_port* port = request->req_primary; + printa(column, "if (!SQLCODE) "); + column += INDENT; + begin(column); + gen_receive(action, column, port); + gen_name(name, port->por_references, true); + printa(column, "if (!SQLCODE && !%s)", name); + printa(column + INDENT, "SQLCODE = 100;"); + endp(column); + column -= INDENT; +} + + +//____________________________________________________________ +// +// Generate a name for a reference. Name is constructed from +// port and parameter idents. +// + +static TEXT* gen_name(char* const string, const ref* reference, bool as_blob) +{ + if (reference->ref_field->fld_array_info && !as_blob) + fb_utils::snprintf(string, MAX_REF_SIZE, "fb_%d", + reference->ref_field->fld_array_info->ary_ident); + else if (reference->ref_port) + fb_utils::snprintf(string, MAX_REF_SIZE, "fb_%d.fb_%d", + reference->ref_port->por_ident, reference->ref_ident); + else + fb_utils::snprintf(string, MAX_REF_SIZE, "fb_%d", reference->ref_ident); + + return string; +} + + +//____________________________________________________________ +// +// Generate a block to handle errors. +// + +static void gen_on_error( const act* action, USHORT column) +{ + const act* err_action = (const act*) action->act_object; + switch (err_action->act_type) + { + case ACT_get_segment: + case ACT_put_segment: + case ACT_endblob: + printa(column, + "if ((!%s->isSuccess()) && (%s->get()[1] != isc_segment) && (%s->get()[1] != isc_segstr_eof))", + global_status_name, global_status_name, global_status_name); + break; + default: + printa(column, "if (!%s->isSuccess())", global_status_name); + } + column += INDENT; + begin(column); +} + + +//____________________________________________________________ +// +// Generate code for an EXECUTE PROCEDURE. +// + +static void gen_procedure( const act* action, int column) +{ + ObjectNotImplemented(); + column += INDENT; + const gpre_req* request = action->act_request; + const gpre_port* in_port = request->req_vport; + const gpre_port* out_port = request->req_primary; + + const gpre_dbb* database = request->req_database; + PAT args; + args.pat_database = database; + args.pat_vector1 = status_vector(action); + args.pat_request = request; + args.pat_port = in_port; + args.pat_port2 = out_port; + const TEXT* pattern; + if (in_port && in_port->por_length) + pattern = + "isc_transact_request (%V1, %RF%DH%RE, %RF%RT%RE, sizeof(%RI), %RI, (short) %PL, (char*) %RF%PI%RE, (short) %QL, (char*) %RF%QI%RE);"; + else + pattern = + "isc_transact_request (%V1, %RF%DH%RE, %RF%RT%RE, sizeof(%RI), %RI, 0, 0, (short) %QL, (char*) %RF%QI%RE);"; + + // Get database attach and transaction started + + if (gpreGlob.sw_auto) + t_start_auto(action, 0, status_vector(action), column, true); + + // Move in input values + + asgn_from(action, request->req_values, column); + + // Execute the procedure + + PATTERN_expand((USHORT) column, pattern, &args); + + set_sqlcode(action, column); + + printa(column, "if (!SQLCODE)"); + column += INDENT; + begin(column); + + // Move out output values + + asgn_to_proc(request->req_references, column); + endp(column); +} + + +//____________________________________________________________ +// +// Generate the code to do a put segment. +// + +static void gen_put_segment( const act* action, int column) +{ + ObjectNotImplemented(); + const TEXT* pattern1 = "%IF%S1 [1] = %ENisc_put_segment (%V1, &%BH, %I1, %I2);"; + + if (!action->act_error) + begin(column); + if (action->act_error || (action->act_flags & ACT_sql)) + begin(column); + + const blb* blob; + if (action->act_flags & ACT_sql) + { + blob = (blb*) action->act_request->req_blobs; + const ref* from = action->act_object; + align(column); + fprintf(gpreGlob.out_file, "fb_%d = %s;", blob->blb_len_ident, from->ref_null_value); + align(column); + fprintf(gpreGlob.out_file, "isc_ftof (%s, fb_%d, fb_%d, fb_%d);", + from->ref_value, blob->blb_len_ident, + blob->blb_buff_ident, blob->blb_len_ident); + } + else + blob = (blb*) action->act_object; + + PAT args; + args.pat_blob = blob; + args.pat_vector1 = status_vector(action); + args.pat_condition = !(action->act_error || (action->act_flags & ACT_sql)); + args.pat_ident1 = blob->blb_len_ident; + args.pat_ident2 = blob->blb_buff_ident; + args.pat_string1 = global_status_name; + PATTERN_expand((USHORT) column, pattern1, &args); + + set_sqlcode(action, column); + + if (action->act_flags & ACT_sql) + endp(column); +} + + +//____________________________________________________________ +// +// Generate BLR/MBLR/etc. in raw, numeric form. Ugly but dense. +// + +static void gen_raw(const UCHAR* blr, int request_length) +{ + TEXT buffer[80]; + + TEXT* p = buffer; + const TEXT* const limit = buffer + 60; + + for (int count = request_length; count; count--) + { + const TEXT c = *blr++; + if ((c >= 'A' && c <= 'Z') || c == '$' || c == '_') + sprintf(p, "'%c'", c); + else + sprintf(p, "%d", c); + while (*p) + p++; + if (count - 1) + *p++ = ','; + if (p < limit) + continue; + *p = 0; + printa(INDENT, "%s", buffer); + p = buffer; + } + + *p = 0; + printa(INDENT, "%s", buffer); +} + + +//____________________________________________________________ +// +// Generate substitution text for READY +// + +static void gen_ready( const act* action, int column) +{ + const TEXT* vector = status_vector(action); + + for (const rdy* ready = (rdy*) action->act_object; ready; ready = ready->rdy_next) + { + const gpre_dbb* db = ready->rdy_database; + const TEXT* filename = ready->rdy_filename; + if (!filename) + filename = db->dbb_runtime; + if ((action->act_error || (action->act_flags & ACT_sql)) && + ready != (rdy*) action->act_object) + { + printa(column, "if (%s->isSuccess()) {", global_status_name); + } + make_ready(db, filename, vector, (USHORT) column, ready->rdy_request); + if ((action->act_error || (action->act_flags & ACT_sql)) && + ready != (rdy*) action->act_object) + { + endp(column); + } + } + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a send or receive call for a port. +// + +static void gen_receive( const act* action, int column, const gpre_port* port) +{ + PAT args; + const TEXT* pattern = + "%RH->receive (%V1, %RL, %PN, %PL, CAST_MSG(&%PI));"; + + args.pat_request = action->act_request; + args.pat_vector1 = status_vector(action); + args.pat_port = port; + PATTERN_expand((USHORT) column, pattern, &args); + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate substitution text for RELEASE_REQUESTS +// For active databases, call isc_release_request. +// for all others, just zero the handle. For the +// release request calls, ignore error returns, which +// are likely if the request was compiled on a database +// which has been released and re-readied. If there is +// a serious error, it will be caught on the next statement. +// + +static void gen_release( const act* action, int column) +{ + ObjectNotImplemented(); + const gpre_dbb* exp_db = (gpre_dbb*) action->act_object; + + for (const gpre_req* request = gpreGlob.requests; request; request = request->req_next) + { + const gpre_dbb* db = request->req_database; + if (exp_db && db != exp_db) + continue; + if (db && request->req_handle && !(request->req_flags & REQ_exp_hand)) + { + printa(column, "if (%s && %s)", db->dbb_name->sym_string, request->req_handle); + printa(column + INDENT, "isc_release_request (%s, &%s);", + global_status_name, request->req_handle); + printa(column, "%s = 0;", request->req_handle); + } + } +} + + +//____________________________________________________________ +// +// Generate definitions associated with a single request. +// + +static void gen_request(const gpre_req* request) +{ + if (!(request->req_flags & (REQ_exp_hand | REQ_sql_blob_open | REQ_sql_blob_create)) && + request->req_type != REQ_slice && request->req_type != REQ_procedure) + { + printa(0, "static Firebird::IRequest*\n %s = 0;\t\t/* request handle */\n", request->req_handle); + } + + // check the case where we need to extend the dpb dynamically at runtime, + // in which case we need dpb length and a pointer to be defined even if + // there is no static dpb defined + + if (request->req_flags & REQ_extend_dpb) + { + printa(0, "static char\n *fb_%dp;", request->req_ident); + if (!request->req_length) + printa(0, "static short\n fb_%dl = %d;", request->req_ident, request->req_length); + } + + if (request->req_type == REQ_create_database) + printa(0, "static %s\n *fb_%dt;", DCL_LONG, request->req_ident); + + if (request->req_flags & (REQ_sql_blob_open | REQ_sql_blob_create)) + printa(0, "static Firebird::IStatement*\n fb_%ds;\t\t/* sql statement handle */", + request->req_ident); + + if (request->req_length) + { + if (request->req_flags & REQ_sql_cursor) + printa(0, "static Firebird::IStatement*\n fb_%ds;\t\t/* sql statement handle */", + request->req_ident); + printa(0, "static %sunsigned\n fb_%dl = %d;", + (request->req_flags & REQ_extend_dpb) ? "" : CONST_STR, + request->req_ident, request->req_length); + printa(0, "static %sunsigned char\n fb_%d [] = {", CONST_STR, request->req_ident); + + const TEXT* string_type = "blr"; + if (gpreGlob.sw_raw) + { + gen_raw(request->req_blr, request->req_length); + + switch (request->req_type) + { + case REQ_create_database: + case REQ_ready: + string_type = "dpb"; + break; + + case REQ_ddl: + string_type = "dyn"; + break; + case REQ_slice: + string_type = "sdl"; + break; + + default: + string_type = "blr"; + } + } + else + switch (request->req_type) + { + case REQ_create_database: + case REQ_ready: + string_type = "dpb"; + if (PRETTY_print_cdb(request->req_blr, gen_blr, 0, 0)) + { + CPR_error("internal error during parameter generation"); + } + break; + + case REQ_ddl: + string_type = "dyn"; + if (PRETTY_print_dyn(request->req_blr, gen_blr, 0, 0)) + { + CPR_error("internal error during dynamic DDL generation"); + } + break; + case REQ_slice: + string_type = "sdl"; + if (PRETTY_print_sdl(request->req_blr, gen_blr, 0, 0)) + { + CPR_error("internal error during SDL generation"); + } + break; + + default: + string_type = "blr"; + if (fb_print_blr(request->req_blr, request->req_length, gen_blr, 0, 0)) + CPR_error("internal error during BLR generation"); + } + printa(INDENT, "};\t/* end of %s string for request fb_%d */\n", + string_type, request->req_ident); + } + + // Print out slice description language if there are arrays associated with request + + for (const gpre_port* port = request->req_ports; port; port = port->por_next) + { + for (const ref* reference = port->por_references; reference; reference = reference->ref_next) + { + if (reference->ref_sdl) + { + printa(0, "static %sunsigned\n fb_%dl = %d;", CONST_STR, + reference->ref_sdl_ident, reference->ref_sdl_length); + printa(0, "static %sunsigned char\n fb_%d [] = {", CONST_STR, reference->ref_sdl_ident); + if (gpreGlob.sw_raw) + gen_raw(reference->ref_sdl, reference->ref_sdl_length); + else if (PRETTY_print_sdl(reference->ref_sdl, gen_blr, 0, 0)) + CPR_error("internal error during SDL generation"); + + printa(INDENT, "};\t/* end of sdl string for request fb_%d */\n", + reference->ref_sdl_ident); + } + } + } + + // Print out any blob parameter blocks required + + for (const blb* blob = request->req_blobs; blob; blob = blob->blb_next) + { + if (blob->blb_bpb_length) + { + printa(0, "static %sunsigned char\n fb_%d [] = {", CONST_STR, blob->blb_bpb_ident); + gen_raw(blob->blb_bpb, blob->blb_bpb_length); + printa(INDENT, "};\n"); + } + } + + // If this is a GET_SLICE/PUT_slice, allocate some variables + + if (request->req_type == REQ_slice) + { + printa(0, "static %s", DCL_LONG); + printa(INDENT, "fb_%dv [%d],", request->req_ident, + MAX(request->req_slice->slc_parameters, 1)); + printa(INDENT, "fb_%ds;", request->req_ident); + } +} + + +//____________________________________________________________ +// +// Generate receive call for a port +// in a store2 statement. +// + +static void gen_return_value( const act* action, int column) +{ + const gpre_req* request = action->act_request; + if (action->act_pair->act_error) + column += INDENT; + align(column); + gen_start(action, request->req_primary, column, true); + const upd* update = (upd*) action->act_object; + const ref* reference = update->upd_references; + gen_receive(action, column, reference->ref_port); +} + + +//____________________________________________________________ +// +// Process routine head. If there are gpreGlob.requests in the +// routine, insert local definitions. +// + +static void gen_routine( const act* action, int column) +{ + column += INDENT; + + for (const gpre_req* request = (gpre_req*) action->act_object; request; + request = request->req_routine) + { + if (request->req_type == REQ_any) + continue; + for (const gpre_port* port = request->req_ports; port; port = port->por_next) + make_port(port, column); + for (const blb* blob = request->req_blobs; blob; blob = blob->blb_next) + { + printa(column, "isc_blob_handle\t\tfb_%d;\t\t/* blob handle */", blob->blb_ident); + printa(column, "char\t\t\tfb_%d [%d];\t/* blob segment */", + blob->blb_buff_ident, blob->blb_seg_length); + printa(column, "unsigned short\tfb_%d;\t\t/* segment length */", blob->blb_len_ident); + } + } +} + + +//____________________________________________________________ +// +// Generate substitution text for END_STREAM. +// + +static void gen_s_end( const act* action, int column) +{ + ObjectNotImplemented(); + if (action->act_error) + begin(column); + + const gpre_req* request = action->act_request; + + if (action->act_type == ACT_close) + column = gen_cursor_close(action, request, column); + + printa(column, "isc_unwind_request (%s, &%s, %s);", status_vector(action), + request->req_handle, request->req_request_level); + + if (action->act_type == ACT_close) + { + endp(column); + column -= INDENT; + } + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate substitution text for FETCH. +// + +static void gen_s_fetch( const act* action, int column) +{ + const gpre_req* request = action->act_request; + + if (request->req_sync) + gen_send(action, request->req_sync, column); + + gen_receive(action, column, request->req_primary); + + if (!action->act_pair && !action->act_error) + endp(column); +} + + +//____________________________________________________________ +// +// Generate text to compile and start a stream. This is +// used both by START_STREAM and FOR +// + +static void gen_s_start( const act* action, int column) +{ + gpre_req* request = action->act_request; + + gen_compile(action, column); + + gpre_port* port = request->req_vport; + if (port) + asgn_from(action, port->por_references, column); + + if (action->act_type == ACT_open) + column = gen_cursor_open(action, request, column); + + if (action->act_error || (action->act_flags & ACT_sql)) + { + make_ok_test(action, request, column); + column += INDENT; + } + + gen_start(action, port, column, false); + + if (action->act_error || (action->act_flags & ACT_sql)) + column -= INDENT; + + if (action->act_type == ACT_open) + { + endp(column); + column -= INDENT; + endp(column); + column -= INDENT; + } + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Substitute for a segment, segment length, or blob handle. +// + +static void gen_segment( const act* action, int column) +{ + const blb* blob = (blb*) action->act_object; + + printa(column, "fb_%d", + (action->act_type == ACT_segment) ? blob->blb_buff_ident : + (action->act_type == ACT_segment_length) ? blob->blb_len_ident : blob->blb_ident); +} + + +//____________________________________________________________ +// +// +// generate code for a singleton select. +// + +static void gen_select( const act* action, int column) +{ + TEXT name[MAX_REF_SIZE]; + + gpre_req* request = action->act_request; + gpre_port* port = request->req_primary; + gen_name(name, request->req_eof, true); + + gen_s_start(action, column); + printa(column, "if (!SQLCODE) "); + column += INDENT; + begin(column); + gen_receive(action, column, port); + printa(column, "if (!SQLCODE)"); + column += INDENT; + begin(column); + printa(column, "if (%s)", name); + column += INDENT; + + begin(column); + gpre_nod* var_list = (gpre_nod*) action->act_object; + if (var_list) + for (int i = 0; i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) var_list->nod_arg[i], column); + } + + endp(column); + + printa(column - INDENT, "else"); + begin(column); + printa(column, "SQLCODE = 100;"); + endp(column); + column -= INDENT; + endp(column); + column -= INDENT; + endp(column); +} + + +//____________________________________________________________ +// +// Generate a send or receive call for a port. +// + +static void gen_send( const act* action, const gpre_port* port, int column) +{ + PAT args; + const TEXT* pattern = + "%RH->send(%V1, %RL, %PN, %PL, CAST_CONST_MSG(&%PI));"; + + args.pat_request = action->act_request; + args.pat_vector1 = status_vector(action); + args.pat_port = port; + PATTERN_expand((USHORT) column, pattern, &args); + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate support for get/put slice statement. +// + +static void gen_slice( const act* action, const ref* var_reference, int column) +{ + ObjectNotImplemented(); + const TEXT* pattern1 = "isc_get_slice (%V1, &%DH, &%RT, &%FR, (short) %N1, \ +(char*) %I1, (short) %N2, %I1v, %I1s, %S5, &isc_array_length);"; + const TEXT* pattern2 = "isc_put_slice (%V1, &%DH, &%RT, &%FR, (short) %N1, \ +(char*) %I1, (short) %N2, %I1v, %I1s, %S5);"; + + const gpre_req* request = action->act_request; + const slc* slice = (slc*) action->act_object; + const gpre_req* parent_request = slice->slc_parent_request; + + // Compute array size + + printa(column, "fb_%ds = %d", request->req_ident, slice->slc_field->fld_array->fld_length); + + const slc::slc_repeat* tail = slice->slc_rpt; + for (const slc::slc_repeat* const end = tail + slice->slc_dimensions; tail < end; ++tail) + { + if (tail->slc_upper != tail->slc_lower) + { + const ref* lower = (ref*) tail->slc_lower->nod_arg[0]; + const ref* upper = (ref*) tail->slc_upper->nod_arg[0]; + if (lower->ref_value) + fprintf(gpreGlob.out_file, " * ( %s - %s + 1)", upper->ref_value, lower->ref_value); + else + fprintf(gpreGlob.out_file, " * ( %s + 1)", upper->ref_value); + } + } + + fprintf(gpreGlob.out_file, ";"); + + // Make assignments to variable vector + + const ref* reference; + for (reference = request->req_values; reference; reference = reference->ref_next) + { + printa(column, "fb_%dv [%d] = %s;", + request->req_ident, reference->ref_id, reference->ref_value); + } + + PAT args; + args.pat_reference = (var_reference ? var_reference : slice->slc_field_ref); + args.pat_request = parent_request; // blob id request + args.pat_vector1 = status_vector(action); // status vector + args.pat_database = parent_request->req_database; // database handle + args.pat_string1 = action->act_request->req_trans; // transaction handle + args.pat_value1 = request->req_length; // slice descr. length + args.pat_ident1 = request->req_ident; // request name + args.pat_value2 = slice->slc_parameters * sizeof(SLONG); // parameter length + + if (!(reference = var_reference)) + reference = (ref*) slice->slc_array->nod_arg[0]; + args.pat_string5 = reference->ref_value; // array name + + PATTERN_expand((USHORT) column, (action->act_type == ACT_get_slice) ? pattern1 : pattern2, &args); + + set_sqlcode(action, column); + if (action->act_flags & ACT_sql) + gen_whenever(action->act_whenever, column); +} + + +//____________________________________________________________ +// +// Generate either a START or START_AND_SEND depending +// on whether or a not a port is present. If this START +// or START_AND_SEND is being generated for a STORE or a +// MODIFY statement, generate PUT_SLICE calls, as well. +// + +static void gen_start(const act* action, + const gpre_port* port, + int column, + bool sending) +{ + const TEXT* pattern1 = + "%RH->startAndSend(%V1, %S1, %RL, %PN, %PL, CAST_CONST_MSG(&%PI));"; + const TEXT* pattern2 = "%RH->start(%V1, %S1, %RL);"; + + if (port && sending) + { + for (const ref* reference = port->por_references; reference; reference = reference->ref_next) + { + if (reference->ref_field->fld_array_info) + gen_get_or_put_slice(action, reference, false, column); + } + } + + PAT args; + args.pat_request = action->act_request; + args.pat_vector1 = status_vector(action); + args.pat_port = port; + args.pat_string1 = request_trans(action, action->act_request); + PATTERN_expand((USHORT) column, port ? pattern1 : pattern2, &args); +} + + +//____________________________________________________________ +// +// Generate text for STORE statement. This includes the compile +// call and any variable initialization required. +// + +static void gen_store( const act* action, int column) +{ + const gpre_req* request = action->act_request; + align(column); + gen_compile(action, column); + + if (action->act_error || (action->act_flags & ACT_sql)) + { + make_ok_test(action, request, column); + column += INDENT; + if (action->act_error) + begin(column); + } + + // Initialize any blob fields + + TEXT name[MAX_REF_SIZE]; + gpre_port* port = request->req_primary; + + for (const ref* reference = port->por_references; reference; reference = reference->ref_next) + { + const gpre_fld* field = reference->ref_field; + if (field->fld_flags & FLD_blob) + printa(column, "%s = fbBlobNull;", gen_name(name, reference, true)); + } +} + + +//____________________________________________________________ +// +// Generate substitution text for START_TRANSACTION. +// + +static void gen_t_start( const act* action, int column) +{ + const TEXT* vector = status_vector(action); + + // if this is a purely default transaction, just let it through + + const gpre_tra* trans; + if (!action || !(trans = (gpre_tra*) action->act_object)) + { + t_start_auto(action, 0, vector, column, false); + return; + } + + // build a complete statement, including tpb's. Ready db's + const tpb* tpb_iterator; + + if (gpreGlob.sw_auto) + for (tpb_iterator = trans->tra_tpb; tpb_iterator; tpb_iterator = tpb_iterator->tpb_tra_next) + { + const gpre_dbb* db = tpb_iterator->tpb_database; + const TEXT* filename = db->dbb_runtime; + if (filename || !(db->dbb_flags & DBB_sqlca)) + { + printa(column, "if (!%s)", db->dbb_name->sym_string); + make_ready(db, filename, vector, (USHORT) (column + INDENT), 0); + } + } + + if (trans->tra_db_count == 1) + { + printa(column, "%s = %s->startTransaction(%s, %d, fb_tpb_%d);", + trans->tra_handle ? trans->tra_handle : gpreGlob.transaction_name, + trans->tra_tpb->tpb_database->dbb_name->sym_string, vector, + trans->tra_tpb->tpb_length, trans->tra_tpb->tpb_ident); + } + else + { + printa(column, "{\t\t// fbComponents scope\n"); + printa(column + INDENT, "DtcStart fbComponents[%d] = {", trans->tra_db_count); + + for (tpb_iterator = trans->tra_tpb; tpb_iterator; tpb_iterator = tpb_iterator->tpb_tra_next) + { + printa(column + INDENT * 2, "{%s, fb_tpb_%d, %d}%s\n", + tpb_iterator->tpb_database->dbb_name->sym_string, + tpb_iterator->tpb_ident, tpb_iterator->tpb_length, + tpb_iterator->tpb_tra_next ? "," : ""); + } + + printa(column + INDENT, " };\n\n"); + + printa(column + INDENT, "%s = fbMaster->getDtc()->start(%s, %d, fbComponents);\n", + trans->tra_handle ? trans->tra_handle : gpreGlob.transaction_name, + vector, trans->tra_db_count); + + printa(column, "}\t\t// end fbComponents scope\n"); + } + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Generate a TPB in the output file +// + +static void gen_tpb(const tpb* tpb_buffer, int column) +{ + TEXT buffer[80]; + SSHORT length; + + printa(column, "static %sunsigned char\n", CONST_STR); + column += INDENT; + TEXT* p = buffer; + + for (length = 0; length < column; length++) + *p++ = ' '; + + sprintf(p, "fb_tpb_%d [%d] = {", tpb_buffer->tpb_ident, tpb_buffer->tpb_length); + while (*p) + p++; + + SSHORT tpb_length = tpb_buffer->tpb_length; + const TEXT* text = (TEXT*) tpb_buffer->tpb_string; + + while (--tpb_length >= 0) + { + const TEXT c = *text++; + if ((c >= 'A' && c <= 'Z') || c == '$' || c == '_') + sprintf(p, "'%c'", c); + else + sprintf(p, "%d", c); + while (*p) + p++; + if (tpb_length) + *p++ = ','; + if (p - buffer > 60) + { + *p = 0; + fprintf(gpreGlob.out_file, " %s\n", buffer); + p = buffer; + for (length = 0; length < column + INDENT; length++) + *p++ = ' '; + *p = 0; + } + } + + fprintf(gpreGlob.out_file, "%s};\n", buffer); +} + + +//____________________________________________________________ +// +// Generate substitution text for COMMIT, ROLLBACK, PREPARE, and SAVE +// + +static void gen_trans( const act* action, int column) +{ + const char* tranText = + action->act_object ? (const TEXT*) action->act_object : gpreGlob.transaction_name; + + switch (action->act_type) + { + case ACT_commit_retain_context: + case ACT_rollback_retain_context: + printa(column, "%s->%sRetaining (%s);", tranText, + action->act_type == ACT_commit_retain_context ? "commit" : "rollback", + status_vector(action)); + break; + default: + printa(column, "%s->%s(%s);", tranText, + (action->act_type == ACT_commit) ? + "commit" : (action->act_type == ACT_rollback) ? "rollback" : "prepare", + status_vector(action)); + } + + set_sqlcode(action, column); +} + + +//____________________________________________________________ +// +// Substitute for a variable reference. +// + +static void gen_type( const act* action, int column) +{ + printa(column, "%"SLONGFORMAT, (SLONG)(IPTR)action->act_object); +} + + +//____________________________________________________________ +// +// Generate substitution text for UPDATE ... WHERE CURRENT OF ... +// Notice this function's first param has member +// action_act_object->upd_port->por_references that's changed. + +static void gen_update( const act* action, int column) +{ + upd* modify = (upd*) action->act_object; + gpre_port* port = modify->upd_port; + asgn_from(action, port->por_references, column); + gen_send(action, port, column); +} + + +//____________________________________________________________ +// +// Substitute for a variable reference. +// + +static void gen_variable( const act* action, int column) +{ + TEXT s[MAX_REF_SIZE]; + + printa(column, "%s", gen_name(s, action->act_object, false)); +} + + +//____________________________________________________________ +// +// Generate tests for any WHENEVER clauses that may have been declared. +// + +static void gen_whenever( const swe* label, int column) +{ + const TEXT* condition = NULL; + + while (label) + { + switch (label->swe_condition) + { + case SWE_error: + condition = "SQLCODE < 0"; + break; + + case SWE_warning: + condition = "SQLCODE > 0 && SQLCODE != 100"; + break; + + case SWE_not_found: + condition = "SQLCODE == 100"; + break; + + default: + // condition undefined + fb_assert(false); + return; + } + align(column); + fprintf(gpreGlob.out_file, "if (%s) goto %s;", condition, label->swe_label); + label = label->swe_next; + } +} + + +//____________________________________________________________ +// +// Generate a declaration of an array in the +// output file. +// + +static void make_array_declaration(ref* reference) +{ + + gpre_fld* field = reference->ref_field; + const TEXT* name = field->fld_symbol->sym_string; + + // Don't generate multiple declarations for the array. V3 Bug 569. + + if (field->fld_array_info->ary_declared) + return; + + field->fld_array_info->ary_declared = true; + const TEXT* dtype; + + switch (field->fld_array_info->ary_dtype) + { + case dtype_short: + dtype = "short"; + break; + + case dtype_long: + dtype = DCL_LONG; + break; + + case dtype_cstring: + case dtype_text: + case dtype_varying: + dtype = "char "; + break; + + case dtype_quad: + dtype = DCL_QUAD; + break; + + // Begin date/time/timestamp + case dtype_sql_date: + dtype = "ISC_DATE"; + break; + + case dtype_sql_time: + dtype = "ISC_TIME"; + break; + + case dtype_timestamp: + dtype = "ISC_TIMESTAMP"; + break; + // End date/time/timestamp + + case dtype_int64: + dtype = "ISC_INT64"; + break; + + case dtype_real: + dtype = "float "; + break; + + case dtype_double: + dtype = "double"; + break; + + default: + { + TEXT s[ERROR_LENGTH]; + fb_utils::snprintf(s, sizeof(s), "datatype %d unknown for field %s", + field->fld_array_info->ary_dtype, name); + CPR_error(s); + return; + } + } + + fprintf(gpreGlob.out_file, "static %s fb_%d", dtype, field->fld_array_info->ary_ident); + + // Print out the dimension part of the declaration + + for (const dim* dimension = field->fld_array_info->ary_dimension; dimension; + dimension = dimension->dim_next) + { + fprintf(gpreGlob.out_file, " [%"SLONGFORMAT"]", dimension->dim_upper - dimension->dim_lower + 1); + } + + if (field->fld_array_info->ary_dtype <= dtype_varying) + fprintf(gpreGlob.out_file, " [%d]", field->fld_array->fld_length); + + // Print out the database field + + fprintf(gpreGlob.out_file, ";\t/* %s */\n", name); +} + + +//____________________________________________________________ +// +// Turn a symbol into a varying string. +// +// CVC: this code in unclear to me: it's advancing sym_string pointer, +// so after this call the pointer is at the position of the null terminator. + +static TEXT* make_name( TEXT* const string, const gpre_sym* symbol) +{ + + if (symbol->sym_type == SYM_delimited_cursor) + { + // All This elaborate code is just to put double quotes around + // the cursor names and to escape any embeded quotes. + int i = 0; + strcpy(string, "\"\\\""); + const char* source = symbol->sym_string; + for (i = strlen(string); *source && i + 4 < MAX_CURSOR_SIZE; i++) + { + if (*source == '\"' || *source == '\'') + { + if (i + 7 >= MAX_CURSOR_SIZE) + break; + + string[i++] = '\\'; + string[i++] = *source; + string[i++] = '\\'; + } + string[i] = *source++; + } + string[i] = 0; + strcat(string, "\\\"\""); + } + else + fb_utils::snprintf(string, MAX_CURSOR_SIZE, "\"%s\"", symbol->sym_string); + + return string; +} + + +//____________________________________________________________ +// +// Generate code to test existence of compiled request with +// active transaction +// + +static void make_ok_test( const act* action, const gpre_req* request, int column) +{ + + if (gpreGlob.sw_auto) + printa(column, "if (%s && %s)", request_trans(action, request), request->req_handle); + else + printa(column, "if (%s)", request->req_handle); +} + + +//____________________________________________________________ +// +// Insert a port record description in output. +// + +static void make_port(const gpre_port* port, int column) +{ + printa(column, "struct fb_%d_struct {", port->por_ident); + + for (const ref* reference = port->por_references; reference; reference = reference->ref_next) + { + align(column + INDENT); + const gpre_fld* field = reference->ref_field; + const TEXT* name; + const gpre_sym* symbol = field->fld_symbol; + if (symbol) + name = symbol->sym_string; + else + name = ""; + if (reference->ref_value && (reference->ref_flags & REF_array_elem)) + field = field->fld_array; + int fld_len = 0; + const TEXT* dtype; + + switch (field->fld_dtype) + { + case dtype_short: + dtype = "short"; + break; + + case dtype_long: + dtype = DCL_LONG; + break; + + case dtype_cstring: + case dtype_text: + dtype = "char "; + if (field->fld_sub_type != 1 || field->fld_length > 1) + fld_len = field->fld_length; + break; + + case dtype_quad: + dtype = DCL_QUAD; + break; + + // Begin date/time/timestamp + case dtype_sql_date: + dtype = "ISC_DATE"; + break; + + case dtype_sql_time: + dtype = "ISC_TIME"; + break; + + case dtype_timestamp: + dtype = "ISC_TIMESTAMP"; + break; + // End date/time/timestamp + + case dtype_int64: + dtype = "ISC_INT64"; + break; + + case dtype_blob: + dtype = "ISC_QUAD"; + break; + + case dtype_real: + dtype = "float "; + break; + + case dtype_double: + dtype = "double"; + break; + + default: + { + TEXT s[ERROR_LENGTH]; + fb_utils::snprintf(s, sizeof(s), "datatype %d unknown for field %s, msg %d", + field->fld_dtype, name, port->por_msg_number); + CPR_error(s); + return; + } + } + if (fld_len) + fprintf(gpreGlob.out_file, " %s fb_%d [%d];\t/* %s */", + dtype, reference->ref_ident, fld_len, name); + else + fprintf(gpreGlob.out_file, " %s fb_%d;\t/* %s */", dtype, reference->ref_ident, name); + } + + printa(column, "} fb_%d;", port->por_ident); +} + + +//____________________________________________________________ +// +// Generate the actual insertion text for a +// ready; +// + +static void make_ready(const gpre_dbb* db, + const TEXT* filename, + const TEXT* vector, USHORT column, + const gpre_req* request) +{ + TEXT s1[32], s2[32]; + + if (request) + { + sprintf(s1, "fb_%dl", request->req_ident); + + if (request->req_flags & REQ_extend_dpb) + sprintf(s2, "fb_%dp", request->req_ident); + else + sprintf(s2, "fb_%d", request->req_ident); + + // if the dpb needs to be extended at runtime to include items + // in host variables, do so here; this assumes that there is + // always a request generated for runtime variables + + if (request->req_flags & REQ_extend_dpb) + { + if (request->req_length) + printa(column, "%s = fb_%d;", s2, request->req_ident); + else + printa(column, "%s = (char*) 0;", s2); + + printa(column, + "isc_expand_dpb (&%s, &%s, isc_dpb_user_name, %s, isc_dpb_password, %s, isc_dpb_sql_role_name, %s, isc_dpb_lc_messages, %s, isc_dpb_lc_ctype, %s, 0);", + s2, s1, db->dbb_r_user ? db->dbb_r_user : "(char*) 0", + db->dbb_r_password ? db->dbb_r_password : "(char*) 0", + db->dbb_r_sql_role ? db->dbb_r_sql_role : "(char*) 0", + db->dbb_r_lc_messages ? db->dbb_r_lc_messages : "(char*) 0", + db->dbb_r_lc_ctype ? db->dbb_r_lc_ctype : "(char*) 0"); + } + } + + // generate the attach database itself + + const TEXT* dpb_size_ptr = "0"; + const TEXT* dpb_ptr = "(char*) 0"; + + align(column); + if (filename) + fprintf(gpreGlob.out_file, "%s = fbProvider->attachDatabase(%s, %s, %s, %s);", + db->dbb_name->sym_string, vector, filename, + (request ? s1 : dpb_size_ptr), (request ? s2 : dpb_ptr)); + else + fprintf(gpreGlob.out_file, + "%s = fbProvider->attachDatabase(%s, \"%s\", %s, %s);", + db->dbb_name->sym_string, vector, db->dbb_filename, + (request ? s1 : dpb_size_ptr), (request ? s2 : dpb_ptr)); + + // if the dpb was extended, free it here + + if (request && request->req_flags & REQ_extend_dpb) + { + if (request->req_length) + printa(column, "if (%s != fb_%d)", s2, request->req_ident); + printa(column + (request->req_length ? INDENT : 0), "isc_free ((char*) %s);", s2); + + // reset the length of the dpb + + printa(column, "%s = %d;", s1, request->req_length); + } +} + + +//____________________________________________________________ +// +// Print a fixed string at a particular column. +// + +static void printa( int column, const char* string, ...) +{ + va_list ptr; + + va_start(ptr, string); + align(column); + vfprintf(gpreGlob.out_file, string, ptr); + va_end(ptr); +} + + +//____________________________________________________________ +// +// Print a fixed string at a particular column. +// + +static void printb( const TEXT* string, ...) +{ + va_list ptr; + + va_start(ptr, string); + vfprintf(gpreGlob.out_file, string, ptr); + va_end(ptr); +} + + +//____________________________________________________________ +// +// Generate the appropriate transaction handle. +// + +static const TEXT* request_trans( const act* action, const gpre_req* request) +{ + + if (action->act_type == ACT_open) + { + const TEXT* trname = ((open_cursor*) action->act_object)->opn_trans; + if (!trname) { + trname = gpreGlob.transaction_name; + } + return trname; + } + + return request ? request->req_trans : gpreGlob.transaction_name; +} + + +//____________________________________________________________ +// +// Generate the appropriate status vector parameter for a gds +// call depending on where or not the action has an error clause. +// + +static const TEXT* status_vector( const act* action) +{ + +// if (action && (action->act_error || (action->act_flags & ACT_sql))) + return global_status_name; + +// return NULL_STATUS; +} + + +//____________________________________________________________ +// +// Generate substitution text for START_TRANSACTION. +// The complications include the fact that all databases +// must be readied, and that everything should stop if +// any thing fails so we don't trash the status vector. +// + +static void t_start_auto(const act* action, + const gpre_req* request, + const TEXT* vector, + int column, + bool test) +{ + const TEXT* trname = request_trans(action, request); + + // find out whether we're using a status vector or not + + const int stat = !strcmp(vector, global_status_name); + + // this is a default transaction, make sure all databases are ready + + begin(column); + + const gpre_dbb* db; + int count; + + if (gpreGlob.sw_auto) + { + TEXT buffer[256]; + buffer[0] = 0; + for (count = 0, db = gpreGlob.isc_databases; db; db = db->dbb_next, count++) + { + const TEXT* filename = db->dbb_runtime; + if (filename || !(db->dbb_flags & DBB_sqlca)) + { + align(column); + fprintf(gpreGlob.out_file, "if (!%s", db->dbb_name->sym_string); + if (stat && buffer[0]) + fprintf(gpreGlob.out_file, " && %s->isSuccess()", vector); + fprintf(gpreGlob.out_file, ")"); + make_ready(db, filename, vector, (USHORT) (column + INDENT), 0); + if (buffer[0]) + strcat(buffer, " && "); + strcat(buffer, db->dbb_name->sym_string); + } + } + if (!buffer[0]) + strcpy(buffer, "1"); + if (test) + printa(column, "if (%s && !%s)", buffer, trname); + else + printa(column, "if (%s)", buffer); + column += INDENT; + } + else + for (count = 0, db = gpreGlob.isc_databases; db; db = db->dbb_next, count++) + ; // empty loop body + + if (count == 1) + { + printa(column, "%s = %s->startTransaction(%s, 0, NULL);", + trname, gpreGlob.isc_databases->dbb_name->sym_string, vector); + } + else + { + printa(column, "{\t\t// fbComponents scope\n"); + printa(column + INDENT, "DtcStart fbComponents[%d] = {", count); + + for (db = gpreGlob.isc_databases; db; db = db->dbb_next) + { + printa(column + INDENT * 2, "{%s, NULL, 0}%s\n", + db->dbb_next->dbb_name->sym_string, db->dbb_next ? "," : ""); + } + + printa(column + INDENT, " };\n\n"); + + printa(column + INDENT, "%s = fbMaster->getDtc()->start(%s, %d, fbComponents);\n", + trname, vector, count); + + printa(column, "}\t\t// end fbComponents scope\n"); + } + + if (gpreGlob.sw_auto) + column -= INDENT; + + endp(column); +} diff --git a/src/include/firebird/Interface.h b/src/include/firebird/Interface.h index d5de70cbb2..76a9a19c83 100644 --- a/src/include/firebird/Interface.h +++ b/src/include/firebird/Interface.h @@ -87,6 +87,7 @@ public: #define FB_STATUS_VERSION (FB_DISPOSABLE_VERSION + 5) class IProvider; +class IUtl; class IPluginManager; class ITimerControl; class IAttachment; @@ -122,8 +123,9 @@ public: virtual IMetadataBuilder* FB_CARG getMetadataBuilder(IStatus* status, unsigned fieldCount) = 0; virtual Firebird::IDebug* FB_CARG getDebug() = 0; virtual int FB_CARG serverMode(int mode) = 0; + virtual IUtl* FB_CARG getUtlInterface() = 0; }; -#define FB_MASTER_VERSION (FB_VERSIONED_VERSION + 13) +#define FB_MASTER_VERSION (FB_VERSIONED_VERSION + 14) } // namespace Firebird diff --git a/src/include/firebird/Provider.h b/src/include/firebird/Provider.h index eb4670fb76..f7a7ce6439 100644 --- a/src/include/firebird/Provider.h +++ b/src/include/firebird/Provider.h @@ -1,6 +1,6 @@ /* * PROGRAM: Firebird basic API - * MODULE: YValvefirebird/Interface.h + * MODULE: firebird/Provider.h * DESCRIPTION: Interfaces, used by yValve * * The contents of this file are subject to the Initial @@ -100,7 +100,7 @@ public: virtual const char* FB_CARG getAlias(IStatus* status, unsigned index) const = 0; virtual unsigned FB_CARG getType(IStatus* status, unsigned index) const = 0; virtual FB_BOOLEAN FB_CARG isNullable(IStatus* status, unsigned index) const = 0; - virtual unsigned FB_CARG getSubType(IStatus* status, unsigned index) const = 0; + virtual int FB_CARG getSubType(IStatus* status, unsigned index) const = 0; virtual unsigned FB_CARG getLength(IStatus* status, unsigned index) const = 0; virtual int FB_CARG getScale(IStatus* status, unsigned index) const = 0; virtual unsigned FB_CARG getCharSet(IStatus* status, unsigned index) const = 0; @@ -116,17 +116,18 @@ class IMetadataBuilder : public IRefCounted { public: virtual void FB_CARG setType(IStatus* status, unsigned index, unsigned type) = 0; - virtual void FB_CARG setSubType(IStatus* status, unsigned index, unsigned subType) = 0; + virtual void FB_CARG setSubType(IStatus* status, unsigned index, int subType) = 0; virtual void FB_CARG setLength(IStatus* status, unsigned index, unsigned length) = 0; virtual void FB_CARG setCharSet(IStatus* status, unsigned index, unsigned charSet) = 0; virtual void FB_CARG setScale(IStatus* status, unsigned index, unsigned scale) = 0; virtual void FB_CARG truncate(IStatus* status, unsigned count) = 0; virtual void FB_CARG moveNameToIndex(IStatus* status, const char* name, unsigned index) = 0; + virtual void FB_CARG remove(IStatus* status, unsigned index) = 0; virtual IMessageMetadata* FB_CARG getMetadata(IStatus* status) = 0; }; -#define FB_METADATA_BUILDER_VERSION (FB_REFCOUNTED_VERSION + 8) +#define FB_METADATA_BUILDER_VERSION (FB_REFCOUNTED_VERSION + 9) class IResultSet : public IRefCounted { diff --git a/src/include/firebird/Utl.h b/src/include/firebird/Utl.h new file mode 100644 index 0000000000..3d3ea75f01 --- /dev/null +++ b/src/include/firebird/Utl.h @@ -0,0 +1,67 @@ +/* + * PROGRAM: Firebird basic API + * MODULE: firebird/Utl.h + * DESCRIPTION: Misc calls + * + * The contents of this file are subject to the Initial + * Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * 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 Alex Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2013 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef FB_UTL_INTERFACE +#define FB_UTL_INTERFACE + +#include "./Interface.h" + +namespace Firebird { + +class IVersionCallback : public IVersioned +{ +public: + virtual void FB_CARG callback(const char* text) = 0; +}; +#define FB_VERSION_CALLBACK_VERSION (FB_VERSIONED_VERSION + 1) + +class IAttachment; +class ITransaction; + +class IUtl : public IVersioned +{ +public: + virtual void FB_CARG version(IStatus* status, IAttachment* att, + IVersionCallback* callback) = 0; + virtual void FB_CARG loadBlob(IStatus* status, ISC_QUAD* blobId, + IAttachment* att, ITransaction* tra, const char* file, FB_BOOLEAN txt) = 0; + virtual void FB_CARG dumpBlob(IStatus* status, ISC_QUAD* blobId, + IAttachment* att, ITransaction* tra, const char* file, FB_BOOLEAN txt) = 0; + virtual FB_BOOLEAN FB_CARG editBlob(IStatus* status, ISC_QUAD* blobId, + IAttachment* att, ITransaction* tra, const char* tempFile = NULL) = 0; + virtual void FB_CARG getPerfCounters(IStatus* status, IAttachment* att, + const char* countersSet, ISC_INT64* counters) = 0; + virtual IAttachment* FB_CARG executeCreateDatabase(Firebird::IStatus* status, + unsigned stmtLength, const char* creatDBstatement, unsigned dialect, + FB_BOOLEAN* stmtIsCreateDb = NULL) = 0; +}; +#define FB_UTL_VERSION (FB_VERSIONED_VERSION + 6) + +} // namespace Firebird + +#endif // FB_UTL_INTERFACE diff --git a/src/isql/extract.epp b/src/isql/extract.epp index 9ad5a5f07f..deb9f6632f 100644 --- a/src/isql/extract.epp +++ b/src/isql/extract.epp @@ -59,6 +59,7 @@ #include "../jrd/ods.h" #include "../common/utils_proto.h" #include "../jrd/constants.h" +//#include "../common/classes/ImplementHelper.h" using MsgFormat::SafeArg; @@ -123,11 +124,9 @@ int EXTRACT_ddl(LegacyTables flag, const SCHAR* tabname) if (!DB) { - if (isc_attach_database(gds_status, 0, isqlGlob.global_Db_name, &DB, 0, NULL)) - { - ISQL_errmsg(gds_status); + DB = fbProvider->attachDatabase(fbStatus, isqlGlob.global_Db_name, 0, NULL); + if (ISQL_errmsg(fbStatus)) return FINI_ERROR; - } did_attach = true; // Make it read owner name to display grantor correctly @@ -153,13 +152,11 @@ int EXTRACT_ddl(LegacyTables flag, const SCHAR* tabname) isqlGlob.printf(NEWLINE); bool did_start = false; - if (!gds_trans) + if (!fbTrans) { - if (isc_start_transaction(gds_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(gds_status); + fbTrans = DB->startTransaction(fbStatus, 0, NULL); + if (ISQL_errmsg(fbStatus)) return FINI_ERROR; - } did_start = true; } @@ -201,20 +198,19 @@ int EXTRACT_ddl(LegacyTables flag, const SCHAR* tabname) SHOW_comments(false); // Let's make this an option later. } - if (gds_trans && did_start) - if (isc_commit_transaction(gds_status, &gds_trans)) - { - ISQL_errmsg(gds_status); + if (fbTrans && did_start) + { + fbTrans->commit(fbStatus); + if (ISQL_errmsg(fbStatus)) return FINI_ERROR; - } + fbTrans = NULL; + } if (DB && did_attach) { - if (isc_detach_database(gds_status, &DB)) - { - ISQL_errmsg(gds_status); + DB->detach(fbStatus); + if (ISQL_errmsg(fbStatus)) return FINI_ERROR; - } DB = 0; } @@ -383,7 +379,7 @@ int EXTRACT_list_table(const SCHAR* relation_name, { isqlGlob.printf("COMPUTED BY "); if (!FLD.RDB$COMPUTED_SOURCE.NULL) - ISQL_print_validation (isqlGlob.Out, &FLD.RDB$COMPUTED_SOURCE, true, gds_trans); + ISQL_print_validation (isqlGlob.Out, &FLD.RDB$COMPUTED_SOURCE, true, fbTrans); } else if (!(fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) && FLD.RDB$SYSTEM_FLAG != 1)) { @@ -488,7 +484,7 @@ int EXTRACT_list_table(const SCHAR* relation_name, } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR } @@ -521,7 +517,7 @@ int EXTRACT_list_table(const SCHAR* relation_name, } END_FOR ON_ERROR - ISQL_errmsg (gds_status); + ISQL_errmsg (fbStatus); return FINI_ERROR; END_ERROR; @@ -539,7 +535,7 @@ int EXTRACT_list_table(const SCHAR* relation_name, } END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return FINI_ERROR; END_ERROR; @@ -631,7 +627,7 @@ static bool extract_rel_constraints(const char* relation_name) END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return false; END_ERROR; @@ -839,7 +835,7 @@ static void get_procedure_args(const char* proc_name) } END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1058,7 +1054,7 @@ static void get_function_args_ods12(const char* func_name, USHORT out_arg) } END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1159,7 +1155,7 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return OBJECT_NOT_FOUND; END_ERROR; } @@ -1190,7 +1186,7 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return OBJECT_NOT_FOUND; END_ERROR; @@ -1216,7 +1212,7 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return OBJECT_NOT_FOUND; END_ERROR; @@ -1235,7 +1231,7 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi first = false; END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return OBJECT_NOT_FOUND; END_ERROR @@ -1253,7 +1249,7 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi first = false; END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return OBJECT_NOT_FOUND; END_ERROR @@ -1270,7 +1266,7 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi first = false; END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return OBJECT_NOT_FOUND; END_ERROR @@ -1287,7 +1283,7 @@ static processing_state list_all_grants2(bool show_role_list, const SCHAR* termi first = false; END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return OBJECT_NOT_FOUND; END_ERROR } @@ -1400,7 +1396,7 @@ static void list_all_procs() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1433,9 +1429,9 @@ static void list_all_procs() END_FOR ON_ERROR - IUTILS_msg_get(GEN_ERR, msg, SafeArg() << isc_sqlcode(gds_status)); + IUTILS_msg_get(GEN_ERR, msg, SafeArg() << isc_sqlcode(fbStatus->get())); STDERROUT(msg); // Statement failed, SQLCODE = %d\n\n - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1480,7 +1476,7 @@ static void list_all_tables(LegacyTables flag, SSHORT default_char_set_id) EXTRACT_list_table (REL.RDB$RELATION_NAME, NULL, false, default_char_set_id); END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); ROLLBACK; return; END_ERROR; @@ -1549,7 +1545,7 @@ static void list_all_triggers() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1609,7 +1605,7 @@ static void list_all_triggers() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1676,7 +1672,7 @@ static void list_check() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -1726,7 +1722,7 @@ static void list_charsets() isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1810,7 +1806,7 @@ static void list_collations() isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -1862,7 +1858,7 @@ static void list_create_db() * the page size in a comment with the extracted db name * **************************************/ - static const SCHAR page_items[] = + static const UCHAR page_items[] = { isc_info_page_size, isc_info_end @@ -1896,7 +1892,7 @@ static void list_create_db() fb_utils::exact_name(DBP.RDB$CHARACTER_SET_NAME)); END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2000,7 +1996,7 @@ static void list_create_db() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2104,7 +2100,7 @@ static void list_domain_table(const SCHAR* table_name, SSHORT default_char_set_i if (!FLD.RDB$VALIDATION_SOURCE.NULL) { isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES); - ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, gds_trans); + ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, fbTrans); } if (FLD.RDB$NULL_FLAG == 1) isqlGlob.printf(" NOT NULL"); @@ -2128,7 +2124,7 @@ static void list_domain_table(const SCHAR* table_name, SSHORT default_char_set_i isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -2243,7 +2239,7 @@ static void list_domains(SSHORT default_char_set_id) isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -2275,12 +2271,12 @@ static void listDomainConstraints() isqlGlob.printf("ALTER DOMAIN %s ADD CONSTRAINT", FLD.RDB$FIELD_NAME); isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES); - ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, gds_trans); + ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, fbTrans); isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -2325,7 +2321,7 @@ static void list_exceptions() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -2379,7 +2375,7 @@ static void list_filters() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2474,7 +2470,7 @@ static void list_foreign() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -2578,7 +2574,7 @@ static void list_functions_legacy() END_FOR ON_ERROR - ISQL_errmsg (gds_status); + ISQL_errmsg (fbStatus); return; END_ERROR; @@ -2664,7 +2660,7 @@ static void list_functions_legacy() END_FOR ON_ERROR - ISQL_errmsg (gds_status); + ISQL_errmsg (fbStatus); return; END_ERROR; @@ -2684,7 +2680,7 @@ static void list_functions_legacy() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -2729,7 +2725,7 @@ static void list_functions_ods12() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2763,9 +2759,9 @@ static void list_functions_ods12() END_FOR ON_ERROR - IUTILS_msg_get(GEN_ERR, msg, SafeArg() << isc_sqlcode(gds_status)); + IUTILS_msg_get(GEN_ERR, msg, SafeArg() << isc_sqlcode(fbStatus->get())); STDERROUT(msg); // Statement failed, SQLCODE = %d\n\n - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2818,7 +2814,7 @@ static void list_generators() } END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2895,7 +2891,7 @@ static void list_indexes() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -2954,7 +2950,7 @@ static void list_package_bodies() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -3017,7 +3013,7 @@ static void list_package_headers() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -3084,7 +3080,7 @@ static void list_views() END_FOR ON_ERROR - ISQL_errmsg (gds_status); + ISQL_errmsg (fbStatus); return; END_ERROR; @@ -3097,7 +3093,7 @@ static void list_views() END_FOR ON_ERROR - ISQL_errmsg(gds_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 9d0454529e..fd3b6838e3 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -161,6 +161,9 @@ const int UNKNOWN_LEN = 20; // Unknown type: %d const int MAX_TERMS = 10; // max # of terms in an interactive cmd +const char* ISQL_COUNTERS_SET = "CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches"; +#define ISQL_COUNTERS 8 + namespace IcuUtil { @@ -193,9 +196,9 @@ namespace IcuUtil }; // Return the number of characters of a string. - static unsigned charLength(SSHORT sqlsubtype, unsigned len, const char* str) + static unsigned charLength(SSHORT charset, unsigned len, const char* str) { - if (sqlsubtype != CS_UNICODE_FSS && sqlsubtype != CS_UTF8) + if (charset != CS_UNICODE_FSS && charset != CS_UTF8) return len; unsigned charLen = 0; @@ -212,10 +215,10 @@ namespace IcuUtil } // Pads a string to a specified column width. - static void pad(char* buffer, SSHORT sqlsubtype, unsigned len, const char* str, unsigned width, + static void pad(char* buffer, SSHORT charset, unsigned len, const char* str, unsigned width, bool right) { - if (sqlsubtype != CS_UNICODE_FSS && sqlsubtype != CS_UTF8) + if (charset != CS_UNICODE_FSS && charset != CS_UTF8) { // Truncate if necessary. if (len > width) @@ -266,13 +269,21 @@ namespace IcuUtil } -static inline bool commit_trans(isc_tr_handle* x) +static inline bool commit_trans(Firebird::ITransaction** x) { - if (isc_commit_transaction (isc_status, x)) { - ISQL_errmsg (isc_status); - isc_rollback_transaction (isc_status, x); + (*x)->commit(fbStatus); + if (ISQL_errmsg (fbStatus)) + { + (*x)->rollback(fbStatus); + if (fbStatus->isSuccess()) + { + *x = NULL; + } + return false; } + + *x = NULL; return true; } @@ -322,13 +333,13 @@ struct ri_actions static processing_state add_row(TEXT*); static processing_state blobedit(const TEXT*, const TEXT* const*); -static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap); +static processing_state bulk_insert_hack(const char* command); static bool bulk_insert_retriever(const char* prompt); static bool check_date(const tm& times); static bool check_time(const tm& times); static bool check_timestamp(const tm& times, const int msec); static size_t chop_at(char target[], const size_t size); -static void col_check(const TEXT*, SSHORT*); +static void col_check(const TEXT*, unsigned*); static void copy_str(TEXT**, const TEXT**, bool*, const TEXT* const, const TEXT* const, literal_string_type); static processing_state copy_table(TEXT*, TEXT*, TEXT*); @@ -365,19 +376,17 @@ static processing_state parse_arg(int, SCHAR**, SCHAR*); //, FILE**); #ifdef DEV_BUILD static processing_state passthrough(const char* cmd); #endif -static SSHORT print_item(TEXT**, XSQLVAR*, const unsigned); +static unsigned print_item(TEXT**, const IsqlVar*, const unsigned); static void print_item_numeric(SINT64, int, int, TEXT*); -static processing_state print_line(XSQLDA*, const unsigned pad[], TEXT line[]); -static void print_performance(const perf64* perf_before); -static processing_state print_sqlda_input(const bool is_selectable); -static void print_sqlda_output(const XSQLDA& sqlda); -static void process_header(const XSQLDA* sqlda, const unsigned pad[], TEXT header[], TEXT header2[]); +static processing_state print_line(Firebird::IMessageMetadata*, UCHAR*, const unsigned pad[], TEXT line[]); +static processing_state print_performance(const SINT64* perf_before); +static void print_message(Firebird::IMessageMetadata* msg, const char* dir); +static void process_header(Firebird::IMessageMetadata*, const unsigned pad[], TEXT header[], TEXT header2[]); static void process_plan(); static SLONG process_record_count(const int statement_type); static SSHORT process_request_type(); -static SLONG* process_sqlda_buffer(XSQLDA* const sqlda, SSHORT nullind[]); -static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], unsigned pad[]); -static int process_statement(const TEXT*, XSQLDA**); +static unsigned process_message_display(Firebird::IMessageMetadata* msg, unsigned pad[]); +static processing_state process_statement(const TEXT*); #ifdef WIN_NT static BOOL CALLBACK query_abort(DWORD); #else @@ -385,12 +394,8 @@ static int query_abort(const int, const int, void*); #endif static bool stdin_redirected(); static void strip_quotes(const TEXT*, TEXT*); -//#ifdef DEV_BUILD -static const char* sqltype_to_string(USHORT); -//#endif +static const char* sqltype_to_string(unsigned); -XSQLDA* global_sqlda; -XSQLDA** global_sqldap; // The dialect spoken by the database, should be 0 when no database is connected. USHORT global_dialect_spoken = 0; USHORT requested_SQL_dialect = SQL_DIALECT_V6; @@ -402,17 +407,16 @@ bool Trusted_auth = false; bool Version_info = false; // Utility transaction handle -static isc_tr_handle D__trans; -static isc_tr_handle M__trans; +static Firebird::ITransaction* D__trans = NULL; +static Firebird::ITransaction* M__trans = NULL; static int global_numbufs; // # of cache buffers on connect -static isc_stmt_handle global_Stmt; -//static FILE* Ofp; -//static FILE* Ifp; +static Firebird::IStatement* global_Stmt = NULL; static SCHAR Password[128]; static SCHAR Charset[128]; static bool Merge_stderr; static ColList global_Cols; +static Firebird::GlobalPtr global_Buffer; static int global_Col_default = 0; // Need to write code for it in the future. static Firebird::GlobalPtr Filelist; //static TEXT Tmpfile[MAXPATHLEN]; @@ -421,7 +425,7 @@ static bool Interrupt_flag = false; static bool Echo = false; static bool Time_display = false; static bool Sqlda_display = false; -static int Exit_value; +static int Exit_value = 0; static bool Interactive = true; static bool Input_file = false; static int Pagelength = 20; @@ -513,6 +517,35 @@ static const ri_actions ri_actions_all[] = }; +static bool startTransaction(Firebird::ITransaction** t, unsigned len = 0, const UCHAR* tpb = NULL) +{ + if (DB && !*t) + { + *t = DB->startTransaction(fbStatus, len, tpb); + if (ISQL_errmsg(fbStatus)) + return false; + } + + return true; +} + +static bool M_Transaction() +{ + return startTransaction(&M__trans); +} + +static bool D_Transaction() +{ + return startTransaction(&D__trans, 5, default_tpb); +} + +// Transaction for all frontend commands +static bool frontendTransaction() +{ + return startTransaction(&fbTrans, 5, default_tpb); +} + + int CLIB_ROUTINE main(int argc, char* argv[]) { /************************************** @@ -599,7 +632,6 @@ int ISQL_main(int argc, char* argv[]) LegacyTables flag = ret == EXTRACT ? SQL_objects : ALL_objects; Exit_value = EXTRACT_ddl(flag, tabname); ISQL_disconnect_database(true); - // isc_detach_database(isc_status, &DB); } else Exit_value = FINI_ERROR; @@ -661,13 +693,8 @@ void ISQL_array_dimensions(const TEXT* fieldname) isqlGlob.printf("["); - // Transaction for all frontend commands - if (DB && !gds_trans) - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return; - } + if (!frontendTransaction()) + return; FOR FDIM IN RDB$FIELD_DIMENSIONS WITH FDIM.RDB$FIELD_NAME EQ fieldname @@ -687,7 +714,7 @@ void ISQL_array_dimensions(const TEXT* fieldname) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -876,7 +903,7 @@ int getNextInputChar() } -void ISQL_errmsg(const ISC_STATUS* status) +bool ISQL_errmsg(Firebird::IStatus* st) { /************************************** * @@ -888,9 +915,10 @@ void ISQL_errmsg(const ISC_STATUS* status) * Report error conditions * Simulate isc_print_status exactly, to control stderr **************************************/ + const ISC_STATUS* status = st->get(); TEXT errbuf[MSG_LENGTH]; - if (Quiet) + if (Quiet && !st->isSuccess()) Exit_value = FINI_ERROR; //else { @@ -899,7 +927,7 @@ void ISQL_errmsg(const ISC_STATUS* status) (vec[0] == isc_arg_gds && vec[1] == 0 && vec[2] != isc_arg_warning) || (vec[0] == isc_arg_gds && vec[1] == 0 && vec[2] == isc_arg_warning && !Warnings)) { - return; + return false; } FB_SQLSTATE_STRING sqlstate; fb_sqlstate(sqlstate, status); @@ -973,10 +1001,12 @@ void ISQL_errmsg(const ISC_STATUS* status) STDERROUT(errbuf); } } + + return !st->isSuccess(); } -void ISQL_warning(ISC_STATUS* status) +void ISQL_warning(Firebird::IStatus* st) { /************************************** * @@ -989,7 +1019,7 @@ void ISQL_warning(ISC_STATUS* status) * Simulate isc_print_status exactly, to control stderr **************************************/ - const ISC_STATUS* vec = status; + const ISC_STATUS* vec = st->get(); fb_assert(vec[1] == 0); // shouldn't be any errors //if (!Quiet) @@ -1013,7 +1043,7 @@ void ISQL_warning(ISC_STATUS* status) } } - status[2] = isc_arg_end; + st->init(); } @@ -1073,15 +1103,8 @@ SSHORT ISQL_get_field_length(const TEXT* field_name) * **************************************/ - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return 0; - } - } + if (!frontendTransaction()) + return 0; SSHORT l = 0; FOR FLD IN RDB$FIELDS WITH @@ -1093,7 +1116,7 @@ SSHORT ISQL_get_field_length(const TEXT* field_name) l = FLD.RDB$CHARACTER_LENGTH; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return 0; END_ERROR; @@ -1120,15 +1143,8 @@ void ISQL_get_character_sets(SSHORT char_set_id, SSHORT collation, bool collate_ const char* notNullStr = not_null ? " NOT NULL" : ""; string[0] = 0; - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return; - } - } + if (!frontendTransaction()) + return; //if (collation) { if (collation || collate_only) @@ -1160,7 +1176,7 @@ void ISQL_get_character_sets(SSHORT char_set_id, SSHORT collation, bool collate_ CST.RDB$CHARACTER_SET_NAME, notNullStr, COL.RDB$COLLATION_NAME); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; #ifdef DEV_BUILD @@ -1188,7 +1204,7 @@ void ISQL_get_character_sets(SSHORT char_set_id, SSHORT collation, bool collate_ sprintf (string, " CHARACTER SET %s%s", CST.RDB$CHARACTER_SET_NAME, notNullStr); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; #ifdef DEV_BUILD @@ -1204,6 +1220,29 @@ void ISQL_get_character_sets(SSHORT char_set_id, SSHORT collation, bool collate_ } +processing_state ISQL_fill_var(IsqlVar* var, + Firebird::IMessageMetadata* msg, + unsigned index, + UCHAR* buf) +{ + var->field = msg->getField(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->relation = msg->getRelation(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->owner = msg->getOwner(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->alias = msg->getAlias(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->subType = msg->getSubType(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->scale = msg->getScale(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->type = msg->getType(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->length = msg->getLength(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->charSet = msg->getCharSet(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + var->nullable = msg->isNullable(fbStatus, index); if (!fbStatus->isSuccess()) return ps_ERR; + + var->nullInd = (short*)&buf[msg->getNullOffset(fbStatus, index)]; if (!fbStatus->isSuccess()) return ps_ERR; + var->value.setPtr = &buf[msg->getOffset(fbStatus, index)]; if (!fbStatus->isSuccess()) return ps_ERR; + + return CONT; +} + + void ISQL_get_default_source(const TEXT* rel_name, TEXT* field_name, ISC_QUAD* blob_id) @@ -1223,17 +1262,10 @@ void ISQL_get_default_source(const TEXT* rel_name, fb_utils::exact_name(field_name); - *blob_id = isc_blob_null; + *blob_id = fbBlobNull; - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return; - } - } + if (!frontendTransaction()) + return; if (rel_name) { @@ -1251,7 +1283,7 @@ void ISQL_get_default_source(const TEXT* rel_name, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -1266,7 +1298,7 @@ void ISQL_get_default_source(const TEXT* rel_name, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -1292,15 +1324,8 @@ SLONG ISQL_get_index_segments(TEXT* segs, *segs = '\0'; - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return 0; - } - } + if (!frontendTransaction()) + return 0; TEXT* const segs_end = segs + buf_size - 1; // Query to get column names @@ -1350,7 +1375,7 @@ SLONG ISQL_get_index_segments(TEXT* segs, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); ROLLBACK; return 0; END_ERROR; @@ -1382,15 +1407,8 @@ bool ISQL_get_base_column_null_flag(const TEXT* view_name, SSHORT save_view_context = view_context; - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return false; - } - } + if (!frontendTransaction()) + return false; /* Using view_name and view_context get the relation name from @@ -1511,15 +1529,8 @@ bool ISQL_get_null_flag(const TEXT* rel_name, bool null_flag = true; - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return false; - } - } + if (!frontendTransaction()) + return false; if (rel_name) { @@ -1553,7 +1564,7 @@ bool ISQL_get_null_flag(const TEXT* rel_name, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return null_flag; END_ERROR; } @@ -1568,7 +1579,7 @@ bool ISQL_get_null_flag(const TEXT* rel_name, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return null_flag; END_ERROR; } @@ -1587,7 +1598,7 @@ void ISQL_disconnect_database(bool nQuietMode) * * Functional description * Disconnect from the current database. First commit work and then - * call isc_detach_database to detach from the database and then zero + * call detach to detach from the database and then zero * out the DB handle. * * Change from miroslavp on 2005.05.06 @@ -1605,17 +1616,27 @@ void ISQL_disconnect_database(bool nQuietMode) // Commit transaction that was started on behalf of the request // Don't worry about the error if one occurs it will be network // related and caught later. - if (DB && gds_trans) - isc_rollback_transaction(isc_status, &gds_trans); + if (DB && fbTrans) + { + fbTrans->rollback(fbStatus); + if (fbStatus->isSuccess()) + fbTrans = NULL; + } // If there is current user statement, free it - // I think option 2 is the right one (DSQL_drop), but who knows if (global_Stmt) - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_drop); + { + global_Stmt->free(fbStatus); + if (fbStatus->isSuccess()) + global_Stmt = NULL; + } // Detach from old database - if (DB) { - isc_detach_database(isc_status, &DB); + if (DB) + { + DB->detach(fbStatus); + if (fbStatus->isSuccess()) + DB = NULL; } // Restore original value of the flag @@ -1627,7 +1648,7 @@ void ISQL_disconnect_database(bool nQuietMode) isqlGlob.global_Db_name[0] = '\0'; D__trans = 0; M__trans = 0; - gds_trans = 0; + fbTrans = 0; // CVC: If we aren't connected to a db anymore, then the db's dialect is reset. // This should fix SF Bug #910430. @@ -1652,15 +1673,8 @@ bool ISQL_is_domain(const TEXT* field_name) **************************************/ bool is_domain = false; - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return false; - } - } + if (!frontendTransaction()) + return false; FOR FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ field_name @@ -1672,7 +1686,7 @@ bool ISQL_is_domain(const TEXT* field_name) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return is_domain; END_ERROR; @@ -1719,7 +1733,7 @@ bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int } END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return false; END_ERROR; } @@ -1761,7 +1775,7 @@ bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int void ISQL_print_validation(FILE* fp, ISC_QUAD* blobid, bool isComputedField, - FB_API_HANDLE trans) + Firebird::ITransaction* trans) { /************************************** * @@ -1783,19 +1797,16 @@ void ISQL_print_validation(FILE* fp, if (blobid->gds_quad_high == 0) return; - UserBlob blob(isc_status); - if (!blob.open(DB, trans, *blobid)) - { - ISQL_errmsg(isc_status); + Firebird::IBlob* blob = DB->openBlob(fbStatus, trans, blobid); + if (ISQL_errmsg(fbStatus)) return; - } bool issql = false; bool firsttime = true; TEXT buffer[BUFFER_LENGTH512]; size_t length; - while (blob.getSegment(sizeof(buffer) - 1, buffer, length)) + while ( (length = blob->getSegment(fbStatus, sizeof(buffer) - 1, buffer)) ) { buffer[length] = 0; const TEXT* p = buffer; @@ -1831,8 +1842,10 @@ void ISQL_print_validation(FILE* fp, IUTILS_printf2(fp, "%s", (isComputedField ? " */" : ")")); } - if (isc_status[1] && isc_status[1] != isc_segstr_eof) - ISQL_errmsg(isc_status); + if ((!fbStatus->isSuccess()) && (fbStatus->get()[1] != isc_segstr_eof)) + ISQL_errmsg(fbStatus); + + blob->close(fbStatus); } @@ -1866,107 +1879,67 @@ static processing_state add_row(TEXT* tabname) // There may have been a commit transaction before we got here - if (!M__trans) + if (!M_Transaction()) { - if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return FAIL; - } + return FAIL; } // Start default transaction for prepare - if (!D__trans) + if (!D_Transaction()) { - if (isc_start_transaction(isc_status, &D__trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - return FAIL; - } + return FAIL; } chop_at(tabname, QUOTEDLENGTH); if (tabname[0] != DBL_QUOTE) IUTILS_make_upper(tabname); - /* - chop_at(tablename, WORDLENGTH); - const bool delimited_yes = tablename[0] == DBL_QUOTE; - TEXT tabname[QUOTEDLENGTH]; - if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) { - IUTILS_copy_SQL_id(tablename, tabname, DBL_QUOTE); - } - else - { - strcpy(tabname, tablename); - IUTILS_make_upper(tabname); - } - */ // Query to obtain relation information - SCHAR string[BUFFER_LENGTH120]; - sprintf(string, "SELECT * FROM %s", tabname); + Firebird::string str2; + str2.printf("SELECT * FROM %s", tabname); - XSQLDA* sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(20))); - memset(sqlda, 0, XSQLDA_LENGTH(20)); - sqlda->version = SQLDA_VERSION1; - sqlda->sqln = 20; - if (isc_dsql_prepare(isc_status, &D__trans, &global_Stmt, 0, string, - isqlGlob.SQL_dialect, sqlda)) - { - ISQL_errmsg(isc_status); - if (sqlda) - ISQL_FREE(sqlda); + global_Stmt = DB->prepare(fbStatus, D__trans, 0, str2.c_str(), isqlGlob.SQL_dialect, + Firebird::IStatement::PREPARE_PREFETCH_METADATA); + if (ISQL_errmsg(fbStatus)) return (SKIP); - } - const SSHORT n_cols = sqlda->sqld; + Firebird::RefPtr msg(Firebird::REF_NO_INCR, global_Stmt->getOutputMetadata(fbStatus)); + if (ISQL_errmsg(fbStatus)) + return (SKIP); - // Reallocate to the proper size if necessary + const unsigned n_cols = msg->getCount(fbStatus); + if (ISQL_errmsg(fbStatus)) + return (SKIP); - if (sqlda->sqld > sqlda->sqln) - { - ISQL_FREE(sqlda); - sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(n_cols))); - memset(sqlda, 0, XSQLDA_LENGTH(n_cols)); - sqlda->version = SQLDA_VERSION1; - sqlda->sqld = sqlda->sqln = n_cols; - - // We must re-describe the sqlda, no need to re-prepare - - if (isc_dsql_describe(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda)) - { - ISQL_errmsg(isc_status); - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_close); - if (sqlda) - ISQL_FREE(sqlda); - return (FAIL); - } - } - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_close); + global_Stmt->free(fbStatus); + if (ISQL_errmsg(fbStatus)) + return (SKIP); // Array for storing select/insert column mapping, colcheck sets it up - SSHORT* colnumber = (SSHORT*) ISQL_ALLOC((SLONG) (n_cols * sizeof(SSHORT))); + Firebird::Array coln; + unsigned* colnumber = coln.getBuffer(n_cols); col_check(tabname, colnumber); // Create the new INSERT statement from the sqlda info - SCHAR* insertstring = (SCHAR*) ISQL_ALLOC((SLONG) (40 + (QUOTEDLENGTH + 4) * n_cols)); + Firebird::string insertstring; // There is a question mark for each column that's known to be updatable. - sprintf(insertstring, "INSERT INTO %s (", tabname); + insertstring.printf("INSERT INTO %s (", tabname); - SSHORT i_cols = 0; + unsigned i_cols = 0; { // scope - char fldname[WORDLENGTH]; char fldfixed[QUOTEDLENGTH]; int i = 0; - for (const XSQLVAR* var = sqlda->sqlvar; i < n_cols; var++, i++) + for (unsigned i = 0; i < n_cols; ++i) { // Skip columns that are computed - if (colnumber[i] != -1) + if (colnumber[i] != ~0u) { - fb_utils::copy_terminate(fldname, var->sqlname, var->sqlname_length + 1); + const char* fldname = msg->getField(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); const bool delimited_yes = fldname[0] == DBL_QUOTE; if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) { @@ -1978,37 +1951,51 @@ static processing_state add_row(TEXT* tabname) IUTILS_make_upper(fldfixed); } - sprintf(insertstring + strlen(insertstring), "%s,", fldfixed); - i_cols++; + // Set i_cols to the number of insert columns + if (i_cols++) + insertstring += ','; + insertstring += fldfixed; } } } // scope - // Set i_cols to the number of insert columns - insertstring[strlen(insertstring) - 1] = ')'; - strcat(insertstring, " VALUES ("); + insertstring += ") VALUES ("; - for (int i = 0; i < n_cols; i++) + for (unsigned i = 0; i < i_cols; i++) { - if (colnumber[i] != -1) - strcat(insertstring, " ?,"); + if (i) + insertstring += ','; + insertstring += '?'; } - // Patch the last character in the string - insertstring[strlen(insertstring) - 1] = ')'; + insertstring += ')'; + // Build metadata for insert message + if (i_cols != n_cols) + { + Firebird::RefPtr bldr(Firebird::REF_NO_INCR, msg->getBuilder(fbStatus)); + if (ISQL_errmsg(fbStatus)) + return (SKIP); - // Allocate INSERT sqlda and null indicators + unsigned i = n_cols; + while (--i >= 0) + { + if (colnumber[i] == ~0u) + { + bldr->remove(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + } + } + msg.assignRefNoIncr(bldr->getMetadata(fbStatus)); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + } - XSQLDA* isqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(i_cols))); - - // ISQL_ALLOC doesn't initialize the structure, so init everything - // to avoid lots of problems. - - memset(isqlda, 0, XSQLDA_LENGTH(i_cols)); - isqlda->version = SQLDA_VERSION1; - isqlda->sqln = isqlda->sqld = i_cols; - - SSHORT* nullind = (SSHORT*) ISQL_ALLOC((SLONG) (i_cols * sizeof(SSHORT))); + // Allocate INSERT buffer + unsigned bSize = msg->getMessageLength(fbStatus); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + UCHAR* msgBuf = global_Buffer->getBuffer(bSize); // For each column, get the type, and length then prompt for a value // and scanf the resulting string into a buffer of the right type. @@ -2017,61 +2004,59 @@ static processing_state add_row(TEXT* tabname) IUTILS_msg_get(ADD_PROMPT, sizeof(infobuf), infobuf); STDERROUT(infobuf); - TEXT msg[MSG_LENGTH]; - SCHAR name[WORDLENGTH]; + TEXT txt[MSG_LENGTH]; + const char* name; bool done = false; while (!done) { - SSHORT* nullp = nullind; - int i = 0; - for (const XSQLVAR* var = sqlda->sqlvar; i < n_cols && !done; var++, i++) + for (unsigned i = 0; i < i_cols && !done; ++i) { - if (colnumber[i] == -1) - continue; + short* nullp = (short*)&msgBuf[msg->getNullOffset(fbStatus, i)]; + if (ISQL_errmsg(fbStatus)) + return (SKIP); + UCHAR* datap = &msgBuf[msg->getOffset(fbStatus, i)]; + if (ISQL_errmsg(fbStatus)) + return (SKIP); - // SQLDA for INSERT statement might have fewer columns - const SSHORT j = colnumber[i]; - XSQLVAR* ivar = &isqlda->sqlvar[j]; - *ivar = *var; - *nullp = 0; - ivar->sqlind = nullp++; - ivar->sqldata = NULL; - { // scope - const SSHORT length = var->sqlname_length; - fb_utils::copy_terminate(name, var->sqlname, length + 1); - } // scope - const SSHORT type = var->sqltype & ~1; - const SSHORT nullable = var->sqltype & 1; + const char* name = msg->getField(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + unsigned type = msg->getType(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + const bool nullable = msg->isNullable(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + unsigned varLength, scale; // Prompt with the name and read the line - switch (type) { case SQL_BLOB: - IUTILS_msg_get(BLOB_PROMPT, msg, SafeArg() << name); + IUTILS_msg_get(BLOB_PROMPT, txt, SafeArg() << name); // Blob: %s, type 'edit' or filename to load> break; case SQL_TYPE_DATE: - IUTILS_msg_get(DATE_PROMPT, msg, SafeArg() << name); + IUTILS_msg_get(DATE_PROMPT, txt, SafeArg() << name); // Enter %s as Y/M/D> break; case SQL_TYPE_TIME: - IUTILS_msg_get(TIME_PROMPT, msg, SafeArg() << name); + IUTILS_msg_get(TIME_PROMPT, txt, SafeArg() << name); // Enter %s as H:M:S> break; case SQL_TIMESTAMP: - IUTILS_msg_get(TIMESTAMP_PROMPT, msg, SafeArg() << name); + IUTILS_msg_get(TIMESTAMP_PROMPT, txt, SafeArg() << name); // Enter %s as Y/MON/D H:MIN:S[.MSEC]> break; default: - IUTILS_msg_get(NAME_PROMPT, msg, SafeArg() << name); + IUTILS_msg_get(NAME_PROMPT, txt, SafeArg() << name); // Enter %s> break; } // On blank line or EOF, break the two loops without doing an insert. - readNextInputLine(msg); + readNextInputLine(txt); getColumn = -1; // We are bypassing getNextInputChar(). if (lastInputLine == NULL || strlen(lastInputLine) == 0) { @@ -2079,11 +2064,11 @@ static processing_state add_row(TEXT* tabname) break; } - const size_t length = strlen(lastInputLine); + unsigned length = strlen(lastInputLine); if (length > MAX_SQL_LENGTH) { - IUTILS_msg_get(BUFFER_OVERFLOW, msg); - STDERROUT(msg); + IUTILS_msg_get(BUFFER_OVERFLOW, txt); + STDERROUT(txt); done = true; break; } @@ -2098,11 +2083,12 @@ static processing_state add_row(TEXT* tabname) // If the user writes NULL, put a null in the column if (!strcmp(cmd, "NULL")) - *ivar->sqlind = -1; + *nullp = -1; else { - *ivar->sqlind = 0; + *nullp = 0; SLONG res; + // Data types SSHORT* smallint; SLONG* integer; @@ -2111,18 +2097,20 @@ static processing_state add_row(TEXT* tabname) float* fvalue; double* dvalue; UCHAR* boolean; + ISC_QUAD* blobid; + vary* avary; + char* achar; tm times; // Initialize the time structure memset(×, 0, sizeof(times)); char msec_str[5] = ""; int msec = 0; - ISC_QUAD* blobid; switch (type) { case SQL_BLOB: - blobid = (ISC_QUAD*) ISQL_ALLOC((SLONG) sizeof(ISC_QUAD)); + blobid = (ISC_QUAD*) datap; if (!strcmp(cmd, "EDIT")) // If edit, we edit a temp file. { @@ -2132,8 +2120,10 @@ static processing_state add_row(TEXT* tabname) else { gds__edit(ftmp.c_str(), 0); - res = BLOB_text_load(blobid, DB, M__trans, ftmp.c_str()); + Firebird::UtlInterfacePtr()->loadBlob(fbStatus, blobid, DB, M__trans, + ftmp.c_str(), FB_TRUE); unlink(ftmp.c_str()); + res = fbStatus->isSuccess(); } } else // Otherwise just load the named file @@ -2142,7 +2132,9 @@ static processing_state add_row(TEXT* tabname) // being loaded. As we aren't sure, we'll // do binary operation - this is NEW data in // the database, not updating existing info - res = BLOB_load(blobid, DB, M__trans, lastInputLine); + Firebird::UtlInterfacePtr()->loadBlob(fbStatus, blobid, DB, M__trans, + lastInputLine, FB_FALSE); + res = fbStatus->isSuccess(); } if (!res) @@ -2150,28 +2142,24 @@ static processing_state add_row(TEXT* tabname) STDERROUT("Unable to load file"); done = true; } - - ivar->sqldata = (SCHAR*) blobid; break; case SQL_FLOAT: - fvalue = (float*) ISQL_ALLOC(sizeof(float)); + fvalue = (float*) datap; if (sscanf(lastInputLine, "%g", fvalue) != 1) { STDERROUT("Input parsing problem"); done = true; } - ivar->sqldata = (SCHAR*) fvalue; break; case SQL_DOUBLE: - dvalue = (double*) ISQL_ALLOC(sizeof(double)); + dvalue = (double*) datap; if (sscanf(lastInputLine, "%lg", dvalue) != 1) { STDERROUT("Input parsing problem"); done = true; } - ivar->sqldata = (SCHAR*) dvalue; break; case SQL_TYPE_DATE: @@ -2179,17 +2167,16 @@ static processing_state add_row(TEXT* tabname) ×.tm_mon, ×.tm_mday) || !check_date(times)) { - IUTILS_msg_get(DATE_ERR, msg, SafeArg() << lastInputLine); - STDERROUT(msg); // Bad date %s\n + IUTILS_msg_get(DATE_ERR, txt, SafeArg() << lastInputLine); + STDERROUT(txt); // Bad date %s\n done = true; } else { --times.tm_mon; times.tm_year -= 1900; // tm_year is 1900-based. - ISC_DATE* date = (ISC_DATE*) ISQL_ALLOC((SLONG) ivar->sqllen); + ISC_DATE* date = (ISC_DATE*) datap; isc_encode_sql_date(×, date); - ivar->sqldata = (char*) date; } break; @@ -2198,15 +2185,14 @@ static processing_state add_row(TEXT* tabname) ×.tm_min, ×.tm_sec) || !check_time(times)) { - IUTILS_msg_get(TIME_ERR, msg, SafeArg() << lastInputLine); - STDERROUT(msg); // Bad time %s\n + IUTILS_msg_get(TIME_ERR, txt, SafeArg() << lastInputLine); + STDERROUT(txt); // Bad time %s\n done = true; } else { - ISC_TIME* vtime = (ISC_TIME*) ISQL_ALLOC((SLONG) ivar->sqllen); + ISC_TIME* vtime = (ISC_TIME*) datap; isc_encode_sql_time(×, vtime); - ivar->sqldata = (char*) vtime; } break; @@ -2237,59 +2223,56 @@ static processing_state add_row(TEXT* tabname) done = true; if (done) { - IUTILS_msg_get(TIMESTAMP_ERR, msg, SafeArg() << lastInputLine); - STDERROUT(msg); // Bad timestamp %s\n + IUTILS_msg_get(TIMESTAMP_ERR, txt, SafeArg() << lastInputLine); + STDERROUT(txt); // Bad timestamp %s\n } else { --times.tm_mon; times.tm_year -= 1900; - ISC_TIMESTAMP* datetime = - (ISC_TIMESTAMP*) ISQL_ALLOC((SLONG) ivar->sqllen); + ISC_TIMESTAMP* datetime = (ISC_TIMESTAMP*) datap; isc_encode_timestamp(×, datetime); datetime->timestamp_time += msec; - ivar->sqldata = (char*) datetime; } break; case SQL_TEXT: - // Force all text to varying - ivar->sqltype = SQL_VARYING + nullable; - // Fall into: - case SQL_VARYING: + varLength = msg->getLength(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + if (length > varLength) { - vary* avary = - (vary*) ISQL_ALLOC((SLONG) (length + sizeof(USHORT))); - avary->vary_length = length; - strncpy(avary->vary_string, lastInputLine, length); - ivar->sqldata = (SCHAR*) avary; - ivar->sqllen = length; - break; + STDERROUT("String too long"); // msg! + length = varLength; } + if (type == SQL_VARYING) + { + avary = (vary*) datap; + avary->vary_length = length; + memcpy(avary->vary_string, lastInputLine, length); + } + else + { + achar = (char*) datap; + memcpy(achar, lastInputLine, length); + memset(&achar[length], ' ', varLength - length); + } + break; + case SQL_SHORT: case SQL_LONG: - if (type == SQL_SHORT) - { - smallint = (SSHORT*) ISQL_ALLOC(sizeof(SSHORT)); - ivar->sqldata = (SCHAR*) smallint; - } - else if (type == SQL_LONG) - { - integer = (SLONG*) ISQL_ALLOC(sizeof(SLONG)); - ivar->sqldata = (SCHAR*) integer; - } - // Fall into: - - // Fall through from SQL_SHORT and SQL_LONG ... case SQL_INT64: - if (type == SQL_INT64) - { - pi64 = (SINT64*) ISQL_ALLOC(sizeof(SINT64)); - ivar->sqldata = (SCHAR*) pi64; - } - if (ivar->sqlscale < 0) + smallint = (SSHORT*) datap; + integer = (SLONG*) datap; + pi64 = (SINT64*) datap; + + scale = msg->getScale(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + + if (scale < 0) { SSHORT lscale = 0; if (!get_numeric((UCHAR*) lastInputLine, length, &lscale, &n)) @@ -2299,13 +2282,13 @@ static processing_state add_row(TEXT* tabname) } else { - int dscale = ivar->sqlscale - lscale; + int dscale = scale - lscale; if (dscale > 0) { TEXT err_buf[256]; sprintf(err_buf, "input error: input scale %d exceeds the field's scale %d", - -lscale, -ivar->sqlscale); + -lscale, -scale); STDERROUT(err_buf); done = true; } @@ -2346,8 +2329,7 @@ static processing_state add_row(TEXT* tabname) break; case SQL_BOOLEAN: - boolean = (UCHAR*) ISQL_ALLOC(sizeof(UCHAR)); - ivar->sqldata = (SCHAR*) boolean; + boolean = (FB_BOOLEAN*) datap; if (fb_utils::stricmp(lastInputLine, "TRUE") == 0) *boolean = 1; @@ -2369,43 +2351,17 @@ static processing_state add_row(TEXT* tabname) } if (!done) { - // having completed all columns, try the insert statement with the isqlda + // having completed all columns, try the insert statement with the msg - if (isc_dsql_exec_immed2(isc_status, &DB, &M__trans, 0, - insertstring, isqlGlob.SQL_dialect, isqlda, NULL)) + DB->execute(fbStatus, M__trans, insertstring.length(), insertstring.c_str(), + isqlGlob.SQL_dialect, msg, msgBuf, NULL, NULL); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); break; } - - // Release each of the variables for next time - XSQLVAR* ivar = isqlda->sqlvar; - for (int i = 0; i < i_cols; i++, ivar++) - { - if (ivar->sqldata) - ISQL_FREE(ivar->sqldata); - } } } - ISQL_FREE(insertstring); - ISQL_FREE(sqlda); - if (isqlda) - { - XSQLVAR* ivar = isqlda->sqlvar; - for (int i = 0; i < i_cols; i++, ivar++) - { - if (ivar->sqldata) - ISQL_FREE(ivar->sqldata); - } - - ISQL_FREE(isqlda); - } - if (colnumber) - ISQL_FREE(colnumber); - if (nullind) - ISQL_FREE(nullind); - return (SKIP); } @@ -2441,7 +2397,7 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) processing_state rc = SKIP; if (!strcmp(action, "BLOBVIEW")) - BLOB_edit(&blobid, DB, M__trans, "blob"); + Firebird::UtlInterfacePtr()->editBlob(fbStatus, &blobid, DB, M__trans, "blob"); else if ((!strcmp(action, "BLOBDUMP")) && (*cmd[2])) { // If this is a blobdump, make sure there is a file name @@ -2449,17 +2405,16 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) // as we're not sure, we'll dump it in BINARY mode. TEXT path[MAXPATHLEN]; strip_quotes(cmd[2], path); - if (!BLOB_dump(&blobid, DB, M__trans, path)) - { - TEXT errbuf[MSG_LENGTH]; - IUTILS_msg_get(FILE_OPEN_ERR, errbuf, SafeArg() << path); - STDERROUT(errbuf); - rc = ps_ERR; - } + Firebird::UtlInterfacePtr()->dumpBlob(fbStatus, &blobid, DB, M__trans, path, FB_FALSE); } else rc = ps_ERR; + if (rc == SKIP && ISQL_errmsg(fbStatus)) + { + rc = ps_ERR; + } + return rc; } @@ -2492,7 +2447,7 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd) // like #, $, %, etc., and anything can follow them, including random text. // It's invalid to put +++ (signaling row continuation) after a tuple is complete. // If you came here looking for robust parsing code, you're at the wrong place. -static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) +static processing_state bulk_insert_hack(const char* command) { // Skip "SET BULK_INSERT" part. for (int j = 0; j < 2; ++j) @@ -2507,45 +2462,38 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) processing_state ret = SKIP; - // We received the address of the sqlda in case we realloc it - XSQLDA* sqlda = *sqldap; - // If somebody did a commit or rollback, we are out of a transaction - if (!M__trans) - { - if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL)) - ISQL_errmsg(isc_status); - } + M_Transaction(); // No need to start a default transaction unless there is no current one - if (Autocommit && !D__trans) + if (Autocommit) { - if (isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb)) - { - ISQL_errmsg(isc_status); - } + D_Transaction(); } // If statistics are requested, then reserve them here - perf64 perf_before; + SINT64 perf_before[ISQL_COUNTERS]; if (Stats) - perf64_get_info(&DB, &perf_before); + { + Firebird::UtlInterfacePtr()->getPerfCounters(fbStatus, + DB, ISQL_COUNTERS_SET, perf_before); + if (ISQL_errmsg(fbStatus)) + { + return ps_ERR; + } + } // Prepare the dynamic query stored in string. // But put this on the DDL transaction to get maximum visibility of // metadata. - FB_API_HANDLE prepare_trans; - if (Autocommit) - prepare_trans = D__trans; - else - prepare_trans = M__trans; - - if (isc_dsql_prepare(isc_status, &prepare_trans, &global_Stmt, 0, command, - isqlGlob.SQL_dialect, sqlda)) + global_Stmt = DB->prepare(fbStatus, Autocommit ? D__trans : M__trans, 0, command, + isqlGlob.SQL_dialect, Firebird::IStatement::PREPARE_PREFETCH_METADATA | + (Plan ? Firebird::IStatement::PREPARE_PREFETCH_DETAILED_PLAN : 0)); + if (!fbStatus->isSuccess()) { if (isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION && Input_file) { @@ -2557,13 +2505,13 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) command, NEWLINE); } - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; } // check for warnings - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); // Find out what kind of statement this is const int statement_type = process_request_type(); @@ -2576,71 +2524,28 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) return ps_ERR; } -//#ifdef DEV_BUILD - if (Sqlda_display) + Firebird::RefPtr message(global_Stmt->getInputMetadata(fbStatus)); + if (ISQL_errmsg(fbStatus)) { - const bool can_have_input_parameters = - statement_type == isc_info_sql_stmt_select || - statement_type == isc_info_sql_stmt_insert || - statement_type == isc_info_sql_stmt_update || - statement_type == isc_info_sql_stmt_delete || - statement_type == isc_info_sql_stmt_exec_procedure || - statement_type == isc_info_sql_stmt_select_for_upd; - - if (print_sqlda_input(can_have_input_parameters) == ps_ERR) - return ps_ERR; - } -//#endif // DEV_BUILD - - if (isc_dsql_describe_bind(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda)) - { - ISQL_errmsg(isc_status); return ps_ERR; } - // check for warnings - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); + if (Sqlda_display) + { + print_message(message, "IN"); + } - if (!sqlda->sqld) // No input parameters, doesn't make sense for bulk insertions. + const unsigned n_cols = message->getCount(fbStatus); + if (ISQL_errmsg(fbStatus)) + { + return ps_ERR; + } + if (!n_cols) // No input parameters, doesn't make sense for bulk insertions. { isqlGlob.printf("There must be at least one parameter in the statement.%s", NEWLINE); return ps_ERR; } - if (sqlda->sqld > sqlda->sqln) - { - const USHORT n_columns = sqlda->sqld; - ISQL_FREE(sqlda); - *sqldap = sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(n_columns))); - memset(sqlda, 0, XSQLDA_LENGTH(n_columns)); - sqlda->version = SQLDA_VERSION1; - sqlda->sqld = sqlda->sqln = n_columns; - if (isc_dsql_describe_bind(isc_status, &global_Stmt, isqlGlob.SQL_dialect, - sqlda)) - { - ISQL_errmsg(isc_status); - return ps_ERR; - } - else if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); - } - else - { - // Our dear process_statement doesn't clean the global sqlda after using it, - // although it deallocates the sqldata in a single continuous memory section. - const int n_cols = sqlda->sqln; - XSQLVAR* ivar = sqlda->sqlvar; - for (int i = 0; i < n_cols; ++i, ++ivar) - { - if (ivar->sqldata) - { - //ISQL_FREE(ivar->sqldata); Assume everything was deallocated. - ivar->sqldata = 0; - } - } - } - // If PLAN is set, print out the plan now. if (Plan) @@ -2650,25 +2555,10 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) return ret; // Do not execute. } - const int n_cols = sqlda->sqld; - SSHORT* nullind = new SSHORT[n_cols]; - PtrSentry nullindSentry(nullind, true); - - int textFieldCounter = 0; - for (int i = 0; i < n_cols; ++i) - { - switch (sqlda->sqlvar[i].sqltype & ~1) - { - case SQL_TEXT: - case SQL_VARYING: - ++textFieldCounter; - break; - } - } - - // Used to optimize allocations to text fields. - Extender* const stringBuffers = textFieldCounter ? new Extender[textFieldCounter] : 0; - PtrSentry stringbufSentry(stringBuffers, true); + unsigned msgSize = message->getMessageLength(fbStatus); + if (ISQL_errmsg(fbStatus)) + return ps_ERR; + UCHAR* msgBuf = global_Buffer->getBuffer(msgSize); char bulk_prompt[BUFFER_LENGTH180] = ""; IUTILS_msg_get(BULK_PROMPT, sizeof(bulk_prompt), bulk_prompt); @@ -2702,24 +2592,36 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) if (fb_utils::strnicmp(lastInputLine, "COMMIT", 6) == 0 || fb_utils::strnicmp(lastInputLine, "COMMIT WORK", 11) == 0) { - if (isc_commit_transaction(isc_status, &M__trans) || - isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL)) + done = true; + + if (!commit_trans(&M__trans)) { - ISQL_errmsg(isc_status); - done = true; - commit_failureM = M__trans ? true : false; + commit_failureM = true; break; // We failed to commit, quit the bulk insertion. } - // CVC: Commit may fail with AUTO-DDL off and DDL changes rejected by DFW. - if (D__trans && commit_trans(&D__trans) && - isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb)) + if (!M_Transaction()) { - ISQL_errmsg(isc_status); - done = true; - commit_failureD = D__trans ? true : false; - break; // We failed to commit, quit the bulk insertion. + commit_failureM = false; + break; // We failed to start transaction, quit the bulk insertion. } + + // CVC: Commit may fail with AUTO-DDL off and DDL changes rejected by DFW. + if (D__trans) + { + if (!commit_trans(&D__trans)) + { + commit_failureD = true; + break; // We failed to commit, quit the bulk insertion. + } + + if (!D_Transaction()) + { + commit_failureD = false; + break; // We failed to commit, quit the bulk insertion. + } + } + done = false; continue; // Go to read another line. } break; // For example, STOP or blank line not inside quoted string. @@ -2736,16 +2638,24 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) const char* lastPos = insert; Extender extender; // Used only for multi-line tuples. - SSHORT* nullp = nullind; - for (int i = 0, textFieldIter = 0; i < n_cols && !done; ++i) + for (unsigned i = 0, textFieldIter = 0; i < n_cols && !done; ++i) { - XSQLVAR* const ivar = &sqlda->sqlvar[i]; - *nullp = 0; - ivar->sqlind = nullp++; - //ivar->sqldata = NULL; Not here because we are reusing fixed size elements. + unsigned offset = message->getNullOffset(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return ps_ERR; + short* nullp = (short*) &msgBuf[offset]; - const SSHORT type = ivar->sqltype & ~1; - const SSHORT nullable = ivar->sqltype & 1; + offset = message->getOffset(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return ps_ERR; + void* datap = &msgBuf[offset]; + + unsigned type = message->getType(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + const bool nullable = message->isNullable(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); const char* finder = 0; int subtract = 0; @@ -2857,7 +2767,7 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) } SCHAR cmd[5] = ""; - const USHORT length = insert - lastPos - subtract; + unsigned length = insert - lastPos - subtract; if (!length && !quote) { // Go ahead with an aux var and see if we hit end of string. @@ -2899,10 +2809,10 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) // If the user writes NULL, put a null in the column. if (!strcmp(cmd, "NULL")) - *ivar->sqlind = -1; + *nullp = -1; else { - *ivar->sqlind = 0; + *nullp = 0; // Data types SSHORT* smallint; SLONG* integer; @@ -2911,76 +2821,65 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) float* fvalue; double* dvalue; UCHAR* boolean; + vary* avary; + char* achar; tm times; // Initialize the time structure. memset(×, 0, sizeof(times)); char msec_str[5] = ""; int msec = 0; ISC_QUAD* blobid; + unsigned varLength, scale; switch (type) { case SQL_BLOB: - - if (ivar->sqldata) - blobid = (ISC_QUAD*) ivar->sqldata; - else - blobid = (ISC_QUAD*) ISQL_ALLOC(sizeof(ISC_QUAD)); + blobid = (ISC_QUAD*) datap; { // scope - UserBlob bs(isc_status); - if (!bs.create(DB, M__trans, *blobid)) + Firebird::IBlob* bs = DB->createBlob(fbStatus, M__trans, blobid); + if (!fbStatus->isSuccess()) { STDERROUT("Unable to create blob"); - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); done = true; break; // Quit the switch() } - size_t real_len; - if (!bs.putData(length, lastPos, real_len) || real_len != length) + bs->putSegment(fbStatus, length, lastPos); + if (!fbStatus->isSuccess()) { STDERROUT("Unable to write to blob"); - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); done = true; } - if (!bs.close()) + bs->close(fbStatus); + if (!fbStatus->isSuccess()) { STDERROUT("Unable to close blob"); - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); done = true; } } // scope - ivar->sqldata = (SCHAR*) blobid; break; case SQL_FLOAT: - if (ivar->sqldata) - fvalue = (float*) ivar->sqldata; - else - fvalue = (float*) ISQL_ALLOC(sizeof(float)); - + fvalue = (float*) datap; if (sscanf(lastPos, "%g", fvalue) != 1) { STDERROUT("Input parsing problem in 'float' value"); done = true; } - ivar->sqldata = (SCHAR*) fvalue; break; case SQL_DOUBLE: - if (ivar->sqldata) - dvalue = (double*) ivar->sqldata; - else - dvalue = (double*) ISQL_ALLOC(sizeof(double)); - + dvalue = (double*) datap; if (sscanf(lastPos, "%lg", dvalue) != 1) { STDERROUT("Input parsing problem in 'double' value"); done = true; } - ivar->sqldata = (SCHAR*) dvalue; break; case SQL_TYPE_DATE: @@ -2996,13 +2895,8 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) { --times.tm_mon; times.tm_year -= 1900; // tm_year is 1900-based. - ISC_DATE* date = (ISC_DATE*) ivar->sqldata; - fb_assert(ivar->sqllen == sizeof(ISC_DATE)); - if (!date) - date = (ISC_DATE*) ISQL_ALLOC((SLONG) ivar->sqllen); - + ISC_DATE* date = (ISC_DATE*) datap; isc_encode_sql_date(×, date); - ivar->sqldata = (char*) date; } break; @@ -3017,13 +2911,8 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) } else { - ISC_TIME* vtime = (ISC_TIME*) ivar->sqldata; - fb_assert(ivar->sqllen == sizeof(ISC_TIME)); - if (!vtime) - vtime = (ISC_TIME*) ISQL_ALLOC((SLONG) ivar->sqllen); - + ISC_TIME* vtime = (ISC_TIME*) datap; isc_encode_sql_time(×, vtime); - ivar->sqldata = (char*) vtime; } break; @@ -3062,69 +2951,49 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) { --times.tm_mon; times.tm_year -= 1900; - ISC_TIMESTAMP* datetime = (ISC_TIMESTAMP*) ivar->sqldata; - fb_assert(ivar->sqllen == sizeof(ISC_TIMESTAMP)); - if (!datetime) - datetime = (ISC_TIMESTAMP*) ISQL_ALLOC((SLONG) ivar->sqllen); - + ISC_TIMESTAMP* datetime = (ISC_TIMESTAMP*) datap; isc_encode_timestamp(×, datetime); datetime->timestamp_time += msec; - ivar->sqldata = (char*) datetime; } break; case SQL_TEXT: - // Force all text to varying - ivar->sqltype = SQL_VARYING + nullable; - // Fall into: - case SQL_VARYING: + varLength = message->getLength(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + if (length > varLength) { - fb_assert(textFieldIter < textFieldCounter); - Extender& field = stringBuffers[textFieldIter++]; - field.alloc(length + sizeof(USHORT)); - vary* avary = reinterpret_cast(field.getBuffer()); - avary->vary_length = length; - strncpy(avary->vary_string, lastPos, length); - ivar->sqldata = (SCHAR*) avary; - ivar->sqllen = length; - break; + STDERROUT("String too long"); // msg! + length = varLength; } + if (type == SQL_VARYING) + { + avary = (vary*) datap; + avary->vary_length = length; + memcpy(avary->vary_string, lastPos, length); + } + else + { + achar = (char*) datap; + memcpy(achar, lastPos, length); + memset(&achar[length], ' ', varLength - length); + } + break; + case SQL_SHORT: case SQL_LONG: - if (type == SQL_SHORT) - { - if (ivar->sqldata) - smallint = (SSHORT*) ivar->sqldata; - else - smallint = (SSHORT*) ISQL_ALLOC(sizeof(SSHORT)); - - ivar->sqldata = (SCHAR*) smallint; - } - else if (type == SQL_LONG) - { - if (ivar->sqldata) - integer = (SLONG*) ivar->sqldata; - else - integer = (SLONG*) ISQL_ALLOC(sizeof(SLONG)); - - ivar->sqldata = (SCHAR*) integer; - } - // Fall into: - - // Fall through from SQL_SHORT and SQL_LONG ... case SQL_INT64: - if (type == SQL_INT64) - { - if (ivar->sqldata) - pi64 = (SINT64*) ivar->sqldata; - else - pi64 = (SINT64*) ISQL_ALLOC(sizeof(SINT64)); + smallint = (SSHORT*) datap; + integer = (SLONG*) datap; + pi64 = (SINT64*) datap; - ivar->sqldata = (SCHAR*) pi64; - } - if (ivar->sqlscale < 0) + scale = message->getScale(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + + if (scale < 0) { SSHORT lscale = 0; if (!get_numeric((UCHAR*) lastPos, length, &lscale, &n)) @@ -3134,13 +3003,13 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) } else { - int dscale = ivar->sqlscale - lscale; + int dscale = scale - lscale; if (dscale > 0) { TEXT err_buf[256]; sprintf(err_buf, "Input error: input scale %d exceeds the field's scale %d", - -lscale, -ivar->sqlscale); + -lscale, -scale); STDERROUT(err_buf); done = true; } @@ -3181,13 +3050,7 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) break; case SQL_BOOLEAN: - if (ivar->sqldata) - boolean = (UCHAR*) ivar->sqldata; - else - boolean = (UCHAR*) ISQL_ALLOC(sizeof(UCHAR)); - - ivar->sqldata = (SCHAR*) boolean; - + boolean = (FB_BOOLEAN*) datap; if (fb_utils::stricmp(lastInputLine, "TRUE") == 0) *boolean = 1; else if (fb_utils::stricmp(lastInputLine, "FALSE") == 0) @@ -3289,21 +3152,16 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) // Having completed all columns, try the insert statement with the sqlda. // This is a non-select DML statement or trans. - if (isc_dsql_execute(isc_status, &M__trans, &global_Stmt, isqlGlob.SQL_dialect, sqlda)) + global_Stmt->execute(fbStatus, M__trans, message, msgBuf, NULL, NULL); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); - done = true; + break; } // Check for warnings. - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); } - - // No need to release each of the variables for next time. - // Text is handled by an array of Extender and the others are fixed size - // and therefore allocated only once. - } // while (!done) if (done) @@ -3312,7 +3170,7 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) if (M__trans) { if (commit_failureM) - isc_rollback_transaction(isc_status, &M__trans); + M__trans->rollback(fbStatus); else commit_trans(&M__trans); } @@ -3320,7 +3178,7 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) if (D__trans) { if (commit_failureD) - isc_rollback_transaction(isc_status, &D__trans); + D__trans->rollback(fbStatus); else commit_trans(&D__trans); } @@ -3337,29 +3195,12 @@ static processing_state bulk_insert_hack(const char* command, XSQLDA** sqldap) ret = ps_ERR; } - // Release each of the variables for next usage. - // The text variables are deallocated separately. - XSQLVAR* ivar = sqlda->sqlvar; - for (int i = 0; i < n_cols; ++i, ++ivar) - { - if ((ivar->sqltype & ~1) != SQL_VARYING && ivar->sqldata) - { - ISQL_FREE(ivar->sqldata); - ivar->sqldata = 0; - } - } - - // Explicit just to test. - stringbufSentry.clean(); - nullindSentry.clean(); - // Get rid of the statement handle. - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_close); + global_Stmt->free(fbStatus); // Statistics printed here upon request - - if (Stats) - print_performance(&perf_before); + if (Stats && (print_performance(perf_before) == ps_ERR)) + ret = ps_ERR; return ret; } @@ -3465,7 +3306,7 @@ static size_t chop_at(char target[], const size_t size) } -static void col_check(const TEXT* tabname, SSHORT* colnumber) +static void col_check(const TEXT* tabname, unsigned* colnumber) { /************************************** * @@ -3478,14 +3319,8 @@ static void col_check(const TEXT* tabname, SSHORT* colnumber) * columns which do not have an equivalent for array or computed cols. **************************************/ - // Transaction for all frontend commands - if (DB && !gds_trans) - { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) { - ISQL_errmsg(isc_status); - return; - } - } + if (!frontendTransaction()) + return; // Query to get array info and computed source not available in the sqlda int i = 0, j = 0; @@ -3496,13 +3331,13 @@ static void col_check(const TEXT* tabname, SSHORT* colnumber) SORTED BY R.RDB$FIELD_POSITION, R.RDB$FIELD_NAME if ((!F.RDB$DIMENSIONS.NULL && F.RDB$DIMENSIONS) || (!F.RDB$COMPUTED_BLR.NULL)) - colnumber[i] = -1; + colnumber[i] = ~0u; else colnumber[i] = j++; ++i; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); END_ERROR; } @@ -3935,26 +3770,13 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name) // execute the create statement // If the isqlGlob.SQL_dialect is not set or set to 2, create the database // as a dialect 3 database. - if (isqlGlob.SQL_dialect == 0 || isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION) + unsigned dialect = + (isqlGlob.SQL_dialect == 0 || isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION) ? + requested_SQL_dialect : isqlGlob.SQL_dialect; + DB = Firebird::UtlInterfacePtr()->executeCreateDatabase(fbStatus, 0, local_statement, dialect); + if (ISQL_errmsg(fbStatus)) { - if (isc_dsql_execute_immediate(isc_status, &DB, &M__trans, 0, - local_statement, requested_SQL_dialect, - NULL)) - { - ISQL_errmsg(isc_status); - if (!DB) - ret = FAIL; - } - } - else - { - if (isc_dsql_execute_immediate(isc_status, &DB, &M__trans, 0, - local_statement, isqlGlob.SQL_dialect, NULL)) - { - ISQL_errmsg(isc_status); - if (!DB) - ret = FAIL; - } + ret = FAIL; } if (DB) @@ -3963,7 +3785,7 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name) SHOW_read_owner(); // No use in cancel when running non-end-user operators - fb_cancel_operation(isc_status, &DB, fb_cancel_disable); + DB->cancelOperation(fbStatus, fb_cancel_disable); // Load isqlGlob.global_Db_name with some value to show a successful attach @@ -4080,21 +3902,13 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name) if (!M__trans) { - if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL)) - ISQL_errmsg(isc_status); + M_Transaction(); if (D__trans) commit_trans(&D__trans); - if (Autocommit && isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb)) - ISQL_errmsg(isc_status); + if (Autocommit) + D_Transaction(); } - // Allocate a new user statement for the database - - if (isc_dsql_allocate_statement(isc_status, &DB, &global_Stmt)) - { - ISQL_errmsg(isc_status); - ret = ps_ERR; - } } if (local_statement) @@ -4144,19 +3958,6 @@ static void do_isql() ISQL_dbcheck(); - // Set up SQLDA for SELECTs, allow up to 20 fields to be selected. - // If we subsequently encounter a query with more columns, we - // will realloc the sqlda as needed - - global_sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(20))); - memset(global_sqlda, 0, XSQLDA_LENGTH(20)); - global_sqlda->version = SQLDA_VERSION1; - global_sqlda->sqln = 20; - - // The sqldap is the handle containing the current sqlda pointer - - global_sqldap = &global_sqlda; - // Read statements and process them from Ifp until the ret // code tells us we are done @@ -4172,17 +3973,41 @@ static void do_isql() if (Abort_flag) { if (D__trans) - isc_rollback_transaction(isc_status, &D__trans); + { + D__trans->rollback(fbStatus); + if (fbStatus->isSuccess()) + D__trans = NULL; + } + if (M__trans) - isc_rollback_transaction(isc_status, &M__trans); - if (gds_trans) - isc_rollback_transaction(isc_status, &gds_trans); + { + M__trans->rollback(fbStatus); + if (fbStatus->isSuccess()) + M__trans = NULL; + } + + if (fbTrans) + { + fbTrans->rollback(fbStatus); + if (fbStatus->isSuccess()) + fbTrans = NULL; + } // If there is current user statement, free it if (global_Stmt) - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_drop); + { + global_Stmt->free(fbStatus); + if (fbStatus->isSuccess()) + global_Stmt = NULL; + } + if (DB) - isc_detach_database(isc_status, &DB); + { + DB->detach(fbStatus); + if (fbStatus->isSuccess()) + DB = NULL; + } + break; } @@ -4226,7 +4051,7 @@ static void do_isql() switch (ret) { case CONT: - if (process_statement(statement, global_sqldap) == ps_ERR) + if (process_statement(statement) == ps_ERR) { Exit_value = FINI_ERROR; if (!Interactive && BailOnError) @@ -4240,11 +4065,25 @@ static void do_isql() if (Abort_flag) { if (D__trans) - isc_rollback_transaction(isc_status, &D__trans); + { + D__trans->rollback(fbStatus); + if (fbStatus->isSuccess()) + D__trans = NULL; + } + if (M__trans) - isc_rollback_transaction(isc_status, &M__trans); - if (gds_trans) - isc_rollback_transaction(isc_status, &gds_trans); + { + M__trans->rollback(fbStatus); + if (fbStatus->isSuccess()) + M__trans = NULL; + } + + if (fbTrans) + { + fbTrans->rollback(fbStatus); + if (fbStatus->isSuccess()) + fbTrans = NULL; + } } else { @@ -4252,35 +4091,66 @@ static void do_isql() commit_trans(&D__trans); if (M__trans) commit_trans(&M__trans); - if (gds_trans) - commit_trans(&gds_trans); + if (fbTrans) + commit_trans(&fbTrans); } - // If there is current user statement, free it + // If there is current user statement, free it // I think DSQL_drop is the right one, but who knows if (global_Stmt) - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_drop); - if (DB) { - isc_detach_database(isc_status, &DB); + { + global_Stmt->free(fbStatus); + if (fbStatus->isSuccess()) + global_Stmt = NULL; } + + if (DB) + { + DB->detach(fbStatus); + if (fbStatus->isSuccess()) + DB = NULL; + } + done = true; break; case BACKOUT: if (D__trans) - isc_rollback_transaction(isc_status, &D__trans); - if (M__trans) - isc_rollback_transaction(isc_status, &M__trans); - if (gds_trans) - isc_rollback_transaction(isc_status, &gds_trans); - - // If there is current user statement, free it - // I think DSQL_drop is the right one, but who knows - if (global_Stmt) - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_drop); - if (DB) { - isc_detach_database(isc_status, &DB); + { + D__trans->rollback(fbStatus); + if (fbStatus->isSuccess()) + D__trans = NULL; } + + if (M__trans) + { + M__trans->rollback(fbStatus); + if (fbStatus->isSuccess()) + M__trans = NULL; + } + + if (fbTrans) + { + fbTrans->rollback(fbStatus); + if (fbStatus->isSuccess()) + fbTrans = NULL; + } + + // If there is current user statement, free it + if (global_Stmt) + { + global_Stmt->free(fbStatus); + if (fbStatus->isSuccess()) + global_Stmt = NULL; + } + + if (DB) + { + DB->detach(fbStatus); + if (fbStatus->isSuccess()) + DB = NULL; + } + done = true; break; @@ -4309,15 +4179,13 @@ static void do_isql() isqlGlob.global_Db_name[0] = '\0'; D__trans = 0; M__trans = 0; - gds_trans = 0; + fbTrans = 0; InputDevices::indev& Ofp = Filelist->Ofp(); // Does it have a valid Temp file pointer? if (Ofp.indev_fpointer) Ofp.drop(); - if (global_sqlda) - ISQL_FREE(global_sqlda); // CVC: If we were halt by an error and Bail, we have pending cleanup. Filelist->clear(); if (lastInputLine) @@ -4341,23 +4209,18 @@ static processing_state drop_db() **************************************/ if (isqlGlob.global_Db_name[0]) { - if (isc_drop_database(isc_status, &DB)) + DB->dropDatabase(fbStatus); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); return (FAIL); } - - // If error occurred and drop database got aborted - if (DB) - return (FAIL); } else return (FAIL); // The database got dropped with or without errors - M__trans = 0; - gds_trans = 0; + fbTrans = 0; global_Stmt = 0; D__trans = 0; @@ -4502,12 +4365,13 @@ static processing_state end_trans() STDERROUT(infobuf); if (DB && M__trans) { - if (isc_commit_transaction(isc_status, &M__trans)) + M__trans->commit(fbStatus); + if (ISQL_errmsg(fbStatus)) { // Commit failed, so roll back anyway - ISQL_errmsg(isc_status); ret = FAIL; } + M__trans = NULL; } } else @@ -4516,11 +4380,12 @@ static processing_state end_trans() STDERROUT(infobuf); if (DB && M__trans) { - if (isc_rollback_transaction(isc_status, &M__trans)) + M__trans->rollback(fbStatus); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); ret = FAIL; } + M__trans = NULL; } } } @@ -4537,11 +4402,12 @@ static processing_state end_trans() // ever doing anything that would cause changes to the dictionary. IUTILS_msg_get(ROLLBACK_MSG, sizeof(infobuf), infobuf); STDERROUT(infobuf); - if (isc_rollback_transaction(isc_status, &M__trans)) + M__trans->rollback(fbStatus); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); ret = FAIL; } + M__trans = NULL; } } } @@ -4550,11 +4416,12 @@ static processing_state end_trans() if (DB && D__trans) { - if (isc_commit_transaction(isc_status, &D__trans)) + D__trans->commit(fbStatus); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); ret = FAIL; } + D__trans = NULL; } return ret; } @@ -4701,8 +4568,8 @@ static processing_state frontend(const TEXT* statement) if (!*cmd) { // In case any default transaction was started - commit it here - if (gds_trans) - commit_trans(&gds_trans); + if (fbTrans) + commit_trans(&fbTrans); return SKIP; } @@ -4719,55 +4586,42 @@ static processing_state frontend(const TEXT* statement) switch (frontoptions.getCommand(parms[0])) { case FrontOptions::show: - // Transaction for all frontend commands - if (DB && !gds_trans) + if (!frontendTransaction()) { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - // Free the frontend command - frontend_free_parms(parms, lparms, parm_defaults); - return FAIL; - } + // Free the frontend command + frontend_free_parms(parms, lparms, parm_defaults); + return FAIL; } ret = SHOW_metadata(parms, lparms); - if (gds_trans) - commit_trans(&gds_trans); + if (fbTrans) + commit_trans(&fbTrans); break; case FrontOptions::add: - if (DB && !gds_trans) + if (!frontendTransaction()) { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - // Free the frontend command - frontend_free_parms(parms, lparms, parm_defaults); - return FAIL; - } + // Free the frontend command + frontend_free_parms(parms, lparms, parm_defaults); + return FAIL; } ret = add_row(lparms[1]); - if (gds_trans) - commit_trans(&gds_trans); + if (fbTrans) + commit_trans(&fbTrans); break; case FrontOptions::copy: - if (DB && !gds_trans) + if (!frontendTransaction()) { - if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) - { - ISQL_errmsg(isc_status); - // Free the frontend command - frontend_free_parms(parms, lparms, parm_defaults); - return FAIL; - } + // Free the frontend command + frontend_free_parms(parms, lparms, parm_defaults); + return FAIL; } ret = copy_table(lparms[1], lparms[2], lparms[3]); - if (gds_trans) - commit_trans(&gds_trans); + if (fbTrans) + commit_trans(&fbTrans); break; case FrontOptions::blobview: @@ -4898,8 +4752,8 @@ static processing_state frontend(const TEXT* statement) } // In case any default transaction was started - commit it here - if (gds_trans) - commit_trans(&gds_trans); + if (fbTrans) + commit_trans(&fbTrans); // Free the frontend command frontend_free_parms(parms, lparms, parm_defaults); @@ -5201,7 +5055,7 @@ static processing_state frontend_set(const char* cmd, const char* const* parms, case SetOptions::bulk_insert: if (*parms[2]) - ret = bulk_insert_hack(cmd, global_sqldap); + ret = bulk_insert_hack(cmd); else ret = ps_ERR; break; @@ -5801,11 +5655,10 @@ void ISQL_get_version(bool call_by_create_db) global_dialect_spoken = 0; - if (isc_database_info(isc_status, &DB, sizeof(db_version_info), - reinterpret_cast(db_version_info), - sizeof(buffer), (SCHAR*) buffer)) + DB->getInfo(fbStatus, sizeof(db_version_info), db_version_info, + sizeof(buffer), buffer); + if(ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); return; } @@ -6011,7 +5864,7 @@ void ISQL_ri_action_print(const TEXT* ri_action_str, } -static bool get_numeric(const UCHAR* string, +static bool get_numeric(const UCHAR* str2, USHORT length, SSHORT* scale, SINT64* ptr) @@ -6023,7 +5876,7 @@ static bool get_numeric(const UCHAR* string, ************************************** * * Functional description - * Convert a numeric literal (string) to its binary value. + * Convert a numeric literal (str2) to its binary value. * * The binary value (int64) is stored at the * address given by ptr. @@ -6034,8 +5887,8 @@ static bool get_numeric(const UCHAR* string, SSHORT local_scale = 0, sign = 0; bool digit_seen = false, fraction = false; - const UCHAR* const end = string + length; - for (const UCHAR* p = string; p < end; p++) + const UCHAR* const end = str2 + length; + for (const UCHAR* p = str2; p < end; p++) { if (DIGIT(*p)) { @@ -6375,7 +6228,7 @@ static processing_state newdb(TEXT* dbname, dpb.insertString(isc_dpb_lc_ctype, ISQL_charset, strlen(ISQL_charset)); } - SSHORT l; + unsigned l; if (l = strlen(local_usr)) { dpb.insertString(isc_dpb_user_name, local_usr, l); } @@ -6406,10 +6259,9 @@ static processing_state newdb(TEXT* dbname, { // scope const TEXT* local_name = isqlGlob.global_Db_name; - if (isc_attach_database(isc_status, 0, local_name, &DB, dpb.getBufferLength(), - reinterpret_cast(dpb.getBuffer()))) + DB = fbProvider->attachDatabase(fbStatus, local_name, dpb.getBufferLength(), dpb.getBuffer()); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); isqlGlob.global_Db_name[0] = '\0'; return FAIL; } @@ -6418,7 +6270,7 @@ static processing_state newdb(TEXT* dbname, SHOW_read_owner(); // No use in cancel when running non-end-user operators - fb_cancel_operation(isc_status, &DB, fb_cancel_disable); + DB->cancelOperation(fbStatus, fb_cancel_disable); } // scope ISQL_get_version(false); @@ -6480,41 +6332,19 @@ static processing_state newdb(TEXT* dbname, if (start_user_trans) { - // Start the user transaction and default transaction if (!M__trans) { - if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL)) - ISQL_errmsg(isc_status); + M_Transaction(); if (D__trans) commit_trans(&D__trans); - if (Autocommit && isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb)) - ISQL_errmsg(isc_status); - } - - - // Allocate a new user statement for this database - - global_Stmt = 0; - if (isc_dsql_allocate_statement(isc_status, &DB, &global_Stmt)) - { - ISQL_errmsg(isc_status); - if (M__trans) - end_trans(); - if (D__trans) - commit_trans(&D__trans); - isc_detach_database(isc_status, &DB); - DB = 0; - isqlGlob.global_Db_name[0] = '\0'; - M__trans = 0; - D__trans = 0; - return FAIL; + if (Autocommit) + D_Transaction(); } } - else { - global_Stmt = 0; - } + + global_Stmt = 0; return SKIP; } @@ -6750,7 +6580,7 @@ static processing_state newtrans(const TEXT* statement) * Functional description * Intercept and handle a set transaction statement by zeroing M__trans * - * Leave the default transaction, gds_trans, alone + * Leave the default transaction, fbTrans, alone * Parameters: The statement in its entirety * **************************************/ @@ -6765,10 +6595,10 @@ static processing_state newtrans(const TEXT* statement) // M__trans = 0 after the commit or rollback. Ready for a new transaction - if (isc_dsql_execute_immediate(isc_status, &DB, &M__trans, 0, statement, - isqlGlob.SQL_dialect, NULL)) + M__trans = DB->execute(fbStatus, NULL, 0, statement, isqlGlob.SQL_dialect, + NULL, NULL, NULL, NULL); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); return FAIL; } @@ -7182,9 +7012,9 @@ static processing_state parse_arg(int argc, SCHAR** argv, SCHAR* tabname) static processing_state passthrough(const char* cmd) { processing_state ret = SKIP; - if (isc_dsql_execute_immediate(isc_status, &DB, &M__trans, 0, cmd, isqlGlob.SQL_dialect, NULL)) + M__trans = DB->execute(fbStatus, M__trans, 0, cmd, isqlGlob.SQL_dialect, NULL, NULL, NULL, NULL); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); if (!DB) ret = FAIL; else @@ -7224,7 +7054,7 @@ static bool checkSpecial(TEXT* const p, const int length, const double value) } -static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) +static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length) { /************************************** * @@ -7241,14 +7071,14 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) TEXT* p = *s; *p = '\0'; - SSHORT dtype = var->sqltype & ~1; // may be modified by print_item_blob() - const int dscale = var->sqlscale; + unsigned dtype = var->type; // may be modified by print_item_blob() + const int dscale = var->scale; if (List) { - isqlGlob.printf("%-31s ", fb_utils::exact_name(var->aliasname)); + isqlGlob.printf("%-31s ", var->alias); } - if ((var->sqltype & 1) && (*var->sqlind < 0)) + if (var->nullable && (*var->nullInd < 0)) { // If field was missing print @@ -7257,17 +7087,17 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) } if (dtype == SQL_TEXT || dtype == SQL_VARYING || dtype == SQL_BOOLEAN) - sprintf(p, "%-*s ", length, ""); + sprintf(p, "%-*.*s ", length, length, ""); else - sprintf(p, "%*s ", length, ""); + sprintf(p, "%*.*s ", length, length, ""); } - else if (!strncmp(var->sqlname, "DB_KEY", 6)) + else if (!strncmp(var->field, "DB_KEY", 6)) { // Special handling for db_keys printed in hex // Keep a temp buf, d for building the binary string - for (const TEXT* t = var->sqldata; t < var->sqldata + var->sqllen; t++) + for (const TEXT* t = var->value.asChar; t < var->value.asChar + var->length; t++) { if (List) { isqlGlob.printf("%02X", (unsigned int) (UCHAR) *t); @@ -7287,7 +7117,7 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) { const ISC_QUAD* blobid; TEXT blobbuf[30]; - TEXT* string; + TEXT* str2; tm times; switch (dtype) @@ -7296,7 +7126,7 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) // Print blob-ids only here - blobid = (ISC_QUAD*) var->sqldata; + blobid = var->value.blobid; sprintf(blobbuf, "%"xLONGFORMAT":%"xLONGFORMAT, blobid->gds_quad_high, blobid->gds_quad_low); sprintf(p, "%17s ", blobbuf); @@ -7306,7 +7136,7 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) // Print blob-ids only here - blobid = (ISC_QUAD*) var->sqldata; + blobid = var->value.blobid; sprintf(blobbuf, "%"xLONGFORMAT":%"xLONGFORMAT, blobid->gds_quad_high, blobid->gds_quad_low); sprintf(p, "%17s ", blobbuf); @@ -7327,13 +7157,13 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) switch (dtype) { case SQL_SHORT: - value = (SINT64) (*(SSHORT*) var->sqldata); + value = *var->value.asSmallint; break; case SQL_LONG: - value = (SINT64) (*(SLONG*) var->sqldata); + value = *var->value.asInteger; break; case SQL_INT64: - value = *(SINT64*) var->sqldata; + value = *var->value.asBigint; break; } @@ -7356,7 +7186,7 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) // one digit more than the specified precision when the value is 0 // The bug appears in TCS DSQL_DOMAIN_12 and 13 // - const double value = (double) *(float*) var->sqldata; + const double value = *var->value.asFloat; if (checkSpecial(p, length, value)) { @@ -7389,7 +7219,7 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) case SQL_DOUBLE: { - const double value = *(double*) var->sqldata; + const double value = *var->value.asDouble; if (checkSpecial(p, length, value)) { @@ -7465,101 +7295,45 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) break; case SQL_TEXT: - string = var->sqldata; - string[var->sqllen] = '\0'; + str2 = var->value.asChar; // See if it is character set OCTETS - if (var->sqlsubtype == 1) + if (var->charSet == 1) { - const ULONG hex_len = 2 * var->sqllen; + const ULONG hex_len = 2 * var->length; TEXT* buff2 = (TEXT*) ISQL_ALLOC(hex_len + 1); // Convert the string to hex digits - for (USHORT i = 0; i < var->sqllen; i++) { - sprintf(&buff2[i * 2], "%02X", (UCHAR)string[i]); + for (unsigned i = 0; i < var->length; i++) { + sprintf(&buff2[i * 2], "%02X", (UCHAR)str2[i]); } buff2[hex_len] = 0; if (List) { - isqlGlob.printf("%s%s", buff2, NEWLINE); + isqlGlob.printf("%-*s%s", var->length, buff2, NEWLINE); } else sprintf(p, "%-*s ", length, buff2); ISQL_FREE(buff2); } else if (List) - isqlGlob.printf("%s%s", string, NEWLINE); + isqlGlob.printf("%-*.*s%s", var->length, var->length, str2, NEWLINE); else { - IcuUtil::pad(p, var->sqlsubtype, strlen(var->sqldata), var->sqldata, length, false); + IcuUtil::pad(p, var->charSet, strnlen(var->value.asChar, var->length), var->value.asChar, length, false); strcat(p, " "); } break; - case SQL_TIMESTAMP: - isc_decode_timestamp((ISC_TIMESTAMP*) var->sqldata, ×); - - if (isqlGlob.SQL_dialect > SQL_DIALECT_V5) - { - sprintf(d, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT, - times.tm_year + 1900, (times.tm_mon + 1), - times.tm_mday, times.tm_hour, times.tm_min, - times.tm_sec, - ((ULONG*) var->sqldata)[1] % ISC_TIME_SECONDS_PRECISION); - } - else - { - if (Time_display) - sprintf(d, "%2d-%s-%4d %2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT, - times.tm_mday, alpha_months[times.tm_mon], - times.tm_year + 1900, times.tm_hour, times.tm_min, - times.tm_sec, - ((ULONG*) var->sqldata)[1] % ISC_TIME_SECONDS_PRECISION); - else - sprintf(d, "%2d-%s-%4d", times.tm_mday, - alpha_months[times.tm_mon], times.tm_year + 1900); - } - - sprintf(p, "%-*s ", length, d); - if (List) { - isqlGlob.printf("%s%s", d, NEWLINE); - } - break; - - case SQL_TYPE_TIME: - isc_decode_sql_time((ISC_TIME*) var->sqldata, ×); - sprintf(d, "%2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT, - times.tm_hour, times.tm_min, times.tm_sec, - (*(ISC_TIME*) var->sqldata) % ISC_TIME_SECONDS_PRECISION); - sprintf(p, "%-*s ", length, d); - if (List) { - isqlGlob.printf("%s%s", d, NEWLINE); - } - break; - - case SQL_TYPE_DATE: - isc_decode_sql_date((ISC_DATE*) var->sqldata, ×); - sprintf(d, "%4.4d-%2.2d-%2.2d", times.tm_year + 1900, - (times.tm_mon + 1), times.tm_mday); - - sprintf(p, "%-*s ", length, d); - if (List) { - isqlGlob.printf("%s%s", d, NEWLINE); - } - break; - case SQL_VARYING: { - vary* avary = (vary*) var->sqldata; - - // Note: we allocated the dataspace 1 byte larger already - avary->vary_string[avary->vary_length] = 0; + vary* avary = var->value.asVary; // If CHARACTER SET OCTETS, print two hex digits per octet - if (var->sqlsubtype == 1) + if (var->charSet == 1) { const ULONG hex_len = 2 * avary->vary_length; char* buff2 = static_cast(ISQL_ALLOC(hex_len + 1)); char* bp = buff2; - for (USHORT i = 0; i < avary->vary_length; i++, bp += 2) + for (unsigned i = 0; i < avary->vary_length; i++, bp += 2) { sprintf(bp, "%02X", static_cast(avary->vary_string[i])); @@ -7578,18 +7352,70 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) ISQL_FREE(buff2); } else if (List) - isqlGlob.printf("%s%s", avary->vary_string, NEWLINE); + isqlGlob.printf("%-*.*s%s", avary->vary_length, avary->vary_length, avary->vary_string, NEWLINE); else { - IcuUtil::pad(p, var->sqlsubtype, avary->vary_length, avary->vary_string, length, false); + IcuUtil::pad(p, var->charSet, avary->vary_length, avary->vary_string, length, false); strcat(p, " "); } break; } + case SQL_TIMESTAMP: + isc_decode_timestamp(var->value.asDateTime, ×); + + if (isqlGlob.SQL_dialect > SQL_DIALECT_V5) + { + sprintf(d, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT, + times.tm_year + 1900, (times.tm_mon + 1), + times.tm_mday, times.tm_hour, times.tm_min, + times.tm_sec, + ((ULONG*) var->value.asChar)[1] % ISC_TIME_SECONDS_PRECISION); + } + else + { + if (Time_display) + sprintf(d, "%2d-%s-%4d %2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT, + times.tm_mday, alpha_months[times.tm_mon], + times.tm_year + 1900, times.tm_hour, times.tm_min, + times.tm_sec, + ((ULONG*) var->value.asChar)[1] % ISC_TIME_SECONDS_PRECISION); + else + sprintf(d, "%2d-%s-%4d", times.tm_mday, + alpha_months[times.tm_mon], times.tm_year + 1900); + } + + sprintf(p, "%-*.*s ", length, length, d); + if (List) { + isqlGlob.printf("%s%s", d, NEWLINE); + } + break; + + case SQL_TYPE_TIME: + isc_decode_sql_time(var->value.asTime, ×); + sprintf(d, "%2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT, + times.tm_hour, times.tm_min, times.tm_sec, + (*var->value.asTime) % ISC_TIME_SECONDS_PRECISION); + sprintf(p, "%-*.*s ", length, length, d); + if (List) { + isqlGlob.printf("%s%s", d, NEWLINE); + } + break; + + case SQL_TYPE_DATE: + isc_decode_sql_date(var->value.asDate, ×); + sprintf(d, "%4.4d-%2.2d-%2.2d", times.tm_year + 1900, + (times.tm_mon + 1), times.tm_mday); + + sprintf(p, "%-*.*s ", length, length, d); + if (List) { + isqlGlob.printf("%s%s", d, NEWLINE); + } + break; + case SQL_BOOLEAN: - strcpy(d, (var->sqldata[0] ? "" : "")); - sprintf(p, "%-*s ", length, d); + strcpy(d, (*var->value.asBoolean ? "" : "")); + sprintf(p, "%-*.*s ", length, length, d); if (List) isqlGlob.printf("%s%s", d, NEWLINE); break; @@ -7613,7 +7439,7 @@ static SSHORT print_item(TEXT** s, XSQLVAR* var, const unsigned length) } -processing_state ISQL_print_item_blob(FILE* fp, const XSQLVAR* var, FB_API_HANDLE trans, int subtype) +processing_state ISQL_print_item_blob(FILE* fp, const IsqlVar* var, Firebird::ITransaction* trans, int subtype) { /****************************************** * @@ -7628,16 +7454,16 @@ processing_state ISQL_print_item_blob(FILE* fp, const XSQLVAR* var, FB_API_HANDL **************************************/ TEXT msg[MSG_LENGTH]; - ISC_QUAD* blobid = (ISC_QUAD*) var->sqldata; + ISC_QUAD* blobid = var->value.blobid; // Don't bother with null blobs if (UserBlob::blobIsNull(*blobid)) return CONT; - if ((var->sqlsubtype != subtype) && (subtype != ALL_BLOBS)) + if ((var->subType != subtype) && (subtype != ALL_BLOBS)) { - IUTILS_msg_get(BLOB_SUBTYPE, msg, SafeArg() << subtype << var->sqlsubtype); + IUTILS_msg_get(BLOB_SUBTYPE, msg, SafeArg() << subtype << var->subType); // Blob display set to subtype %d. This blob: subtype = %d\n IUTILS_printf(fp, msg); return CONT; @@ -7648,7 +7474,7 @@ processing_state ISQL_print_item_blob(FILE* fp, const XSQLVAR* var, FB_API_HANDL UCHAR bpb_buffer[64]; ISC_BLOB_DESC from_desc; - const int blob_subtype = var->sqlsubtype; + const int blob_subtype = var->subType; if (blob_subtype == isc_blob_text) { // ASF: Since ODS11.1, BLOBs are automatically transliterated to the client charset. @@ -7663,23 +7489,16 @@ processing_state ISQL_print_item_blob(FILE* fp, const XSQLVAR* var, FB_API_HANDL // Fix a bug that occur when a BLOB column is added after the start // of the DML transaction. // ASF: But use M__trans if D__trans is not started; - if (!var->relname[0] || - isc_blob_lookup_desc(isc_status, &DB, (D__trans ? &D__trans : &M__trans), - (const UCHAR*) var->relname, (const UCHAR*) var->sqlname, &from_desc, NULL) == 0) - { - if (!var->relname[0]) - { - from_desc.blob_desc_subtype = var->sqlsubtype; - from_desc.blob_desc_charset = var->sqlscale; - } + from_desc.blob_desc_subtype = var->subType; + from_desc.blob_desc_charset = var->charSet; - ISC_BLOB_DESC to_desc; - isc_blob_default_desc(&to_desc, (const UCHAR*) var->relname, (const UCHAR*) var->sqlname); - if (!isc_blob_gen_bpb(isc_status, &to_desc, &from_desc, sizeof(bpb_buffer), - bpb_buffer, &bpb_length)) - { - bpb = bpb_buffer; - } + ISC_BLOB_DESC to_desc; + ISC_STATUS_ARRAY oldStat; + isc_blob_default_desc(&to_desc, (const UCHAR*) var->relation, (const UCHAR*) var->field); + if (!isc_blob_gen_bpb(oldStat, &to_desc, &from_desc, sizeof(bpb_buffer), + bpb_buffer, &bpb_length)) + { + bpb = bpb_buffer; } } } @@ -7690,16 +7509,15 @@ processing_state ISQL_print_item_blob(FILE* fp, const XSQLVAR* var, FB_API_HANDL set_bpb_for_translation(blob_subtype); } - UserBlob blob(isc_status); - if (!blob.open(DB, trans, *blobid, bpb_length, bpb)) + Firebird::IBlob* blob = DB->openBlob(fbStatus, trans, blobid, bpb_length, bpb); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); return ps_ERR; } TEXT buffer[BUFFER_LENGTH512]; size_t length; - while (blob.getSegment(sizeof(buffer) - 1, buffer, length)) + while ( (length = blob->getSegment(fbStatus, sizeof(buffer) - 1, buffer)) ) { // Special displays for blr or acl subtypes @@ -7722,11 +7540,14 @@ processing_state ISQL_print_item_blob(FILE* fp, const XSQLVAR* var, FB_API_HANDL } } - if (isc_status[1] && isc_status[1] != isc_segstr_eof) + if ((!fbStatus->isSuccess()) && (fbStatus->get()[1] != isc_segstr_eof)) { - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); + blob->close(fbStatus); return ps_ERR; } + + blob->close(fbStatus); return CONT; } @@ -7760,8 +7581,8 @@ static void print_item_numeric(SINT64 value, int length, int scale, TEXT* buf) sprintf(buf, "%*" SQUADFORMAT, length, value); // start from LSByte towards MSByte - SSHORT from = length - 1; - SSHORT to = length; + int from = length - 1; + int to = length; // make space for decimal '.' point in buffer buf[to + 1] = '\0'; @@ -7789,7 +7610,7 @@ static void print_item_numeric(SINT64 value, int length, int scale, TEXT* buf) } -static processing_state print_line(XSQLDA* sqlda, const unsigned pad[], TEXT line[]) +static processing_state print_line(Firebird::IMessageMetadata* message, UCHAR* buf, const unsigned pad[], TEXT line[]) { /************************************** * @@ -7805,22 +7626,28 @@ static processing_state print_line(XSQLDA* sqlda, const unsigned pad[], TEXT lin * line = pointer to the line buffer. * **************************************/ - const int maxblob = 20; - XSQLVAR* varlist[maxblob]; // No more than 20 blobs per line + const unsigned maxblob = 20; + IsqlVar varlist[maxblob]; // No more than 20 blobs per line + unsigned varnum = 0; - int varnum = 0; { // scope TEXT* p = line; - int i = 0; - XSQLVAR* var = sqlda->sqlvar; - for (const XSQLVAR* const end = var + sqlda->sqld; var < end; - var++, i++) + unsigned n = message->getCount(fbStatus); + if (ISQL_errmsg(fbStatus)) { + return ps_ERR; + } + for (unsigned i = 0; i < n; ++i) + { + IsqlVar var; + if (ISQL_fill_var(&var, message, i, buf) == ps_ERR) + return ps_ERR; + if (!Interrupt_flag && !Abort_flag) { // Save all the blob vars and print them at the end // CVC: If varnum reaches 20, we must print an error instead of crashing. - const int rc = print_item(&p, var, pad[i]); + const int rc = print_item(&p, &var, pad[i]); if (rc == SQL_BLOB && varnum < maxblob) varlist[varnum++] = var; } @@ -7849,17 +7676,17 @@ static processing_state print_line(XSQLDA* sqlda, const unsigned pad[], TEXT lin // If there were Blobs to print, print them passing the blobid - for (int i = 0; i < varnum; i++) + for (unsigned i = 0; i < varnum; i++) { - const XSQLVAR* var = varlist[i]; - if (!(var->sqltype & 1) || *var->sqlind == 0) + const IsqlVar* var = &varlist[i]; + if (!(var->nullable) || *var->nullInd == 0) { // Print blob title isqlGlob.printf( "==============================================================================%s", NEWLINE); - isqlGlob.printf("%s: %s", var->aliasname, NEWLINE); + isqlGlob.printf("%s: %s", var->alias, NEWLINE); if (ISQL_print_item_blob(isqlGlob.Out, var, M__trans, Doblob) != CONT) return (ps_ERR); isqlGlob.printf( @@ -7875,132 +7702,105 @@ static processing_state print_line(XSQLDA* sqlda, const unsigned pad[], TEXT lin // p r i n t _ p e r f o r m a n c e // ********************************* // As the name implies, show performance as extracted from the API routines. -static void print_performance(const perf64* perf_before) +static processing_state print_performance(const SINT64* perf_before) { // Translation of report strings. Do not remove "static" modifier. static bool have_report = false; - static TEXT report_1[MSG_LENGTH + MSG_LENGTH]; + static Firebird::GlobalPtr diag; - perf64 perf_after; - perf64_get_info(&DB, &perf_after); - if (!have_report) + SINT64 perf_after[ISQL_COUNTERS]; + Firebird::UtlInterfacePtr()->getPerfCounters(fbStatus, DB, ISQL_COUNTERS_SET, perf_after); + if (ISQL_errmsg(fbStatus)) { - IUTILS_msg_get(REPORT1, report_1); - // Current memory = !c\nDelta memory = !d\nMax memory = !x\nElapsed time= !e sec\n - IUTILS_msg_get(REPORT2, &report_1[strlen(report_1)]); - // For GUI_TOOLS: Buffers = !b\nReads = !r\nWrites = !w\nFetches - // = !f\n - // Else: Cpu = !u sec\nBuffers = !b\nReads = !r\nWrites = !w\nFetch - // es = !f\n - have_report = true; - } - SCHAR statistics[256]; - perf64_format(perf_before, &perf_after, report_1, statistics, NULL); - IUTILS_printf2(Diag, "%s%s", statistics, NEWLINE); -} - - -// ********************************* -// p r i n t _ s q l d a _ i n p u t -// ********************************* -// Show the contents of the input sqlda, used typically for parameters. -static processing_state print_sqlda_input(const bool can_have_input_parameters) -{ - // Describe the input - XSQLDA* input_sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(10))); - memset(input_sqlda, 0, XSQLDA_LENGTH(10)); - input_sqlda->version = SQLDA_VERSION1; - input_sqlda->sqld = input_sqlda->sqln = 10; - - if (isc_dsql_describe_bind(isc_status, &global_Stmt, isqlGlob.SQL_dialect, input_sqlda)) - { - ISQL_FREE(input_sqlda); - ISQL_errmsg(isc_status); return ps_ERR; } - else if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); - // Reallocate if there's LOTS of input - if (input_sqlda->sqld > input_sqlda->sqln) + if (!have_report) { - USHORT n_columns = input_sqlda->sqld; - ISQL_FREE(input_sqlda); - input_sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(n_columns))); - memset(input_sqlda, 0, XSQLDA_LENGTH(n_columns)); - input_sqlda->version = SQLDA_VERSION1; - input_sqlda->sqld = input_sqlda->sqln = n_columns; - if (isc_dsql_describe_bind(isc_status, &global_Stmt, isqlGlob.SQL_dialect, input_sqlda)) - { - ISQL_FREE(input_sqlda); - ISQL_errmsg(isc_status); - return ps_ERR; - } - else if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); + TEXT report_1[MSG_LENGTH]; + IUTILS_msg_get(REPORT_NEW1, report_1); + // Current memory = !\nDelta memory = !\nMax memory = !\nElapsed time= ~ sec\n + diag->assign(report_1); + +#ifndef WIN_NT + IUTILS_msg_get(REPORT_NEW2, report_1); + // Cpu = !u sec\n + diag->append(report_1); +#endif + + IUTILS_msg_get(REPORT_NEW3, report_1); + // Buffers = !b\nReads = !r\nWrites = !w\nFetches = !f\n + diag->append(report_1); + + Firebird::string::size_type p; + while ((p = diag->find('!')) != Firebird::string::npos) + diag->replace(p, 1, "%"SQUADFORMAT); + while ((p = diag->find('~')) != Firebird::string::npos) + diag->replace(p, 1, "%"SQUADFORMAT".%.2"SQUADFORMAT); + + have_report = true; } - if (can_have_input_parameters) + struct IsqlStatist { - isqlGlob.printf( - //"\nINPUT SQLDA version: %d sqldaid: %s sqldabc: %ld sqln: %d sqld: %d\n", - "\nINPUT SQLDA version: %d sqln: %d sqld: %d\n", - //input_sqlda->version, input_sqlda->sqldaid, - //input_sqlda->sqldabc, input_sqlda->sqln, - input_sqlda->version, input_sqlda->sqln, - input_sqlda->sqld); - int i = 0; - const XSQLVAR* var = input_sqlda->sqlvar; - for (const XSQLVAR* const end = var + input_sqlda->sqld; var < end; var++, i++) - { - isqlGlob.printf("%02d: sqltype: %d %s %s sqlscale: %d sqlsubtype: %d sqllen: %d\n", - i + 1, var->sqltype, sqltype_to_string(var->sqltype), - (var->sqltype & 1) ? "Nullable" : " ", - var->sqlscale, var->sqlsubtype, var->sqllen); - isqlGlob.printf(" : name: (%d)%*s alias: (%d)%*s\n", - var->sqlname_length, var->sqlname_length, - var->sqlname, var->aliasname_length, - var->aliasname_length, var->aliasname); - isqlGlob.printf(" : table: (%d)%*s owner: (%d)%*s\n", - var->relname_length, var->relname_length, - var->relname, var->ownname_length, - var->ownname_length, var->ownname); - } - } + SINT64 curMem, deltaMem, maxMem; + SINT64 realTime, cpu; + SINT64 buffers, reads, writes, fetches; + + // ISQL_COUNTERS_SET = "CurrentMemory, MaxMemory, RealTime, UserTime, Buffers, Reads, Writes, Fetches" + // 0 1 2 3 4 5 6 7 + IsqlStatist(const SINT64* b, const SINT64* a) + { + curMem = a[0]; + deltaMem = a[0] - b[0]; + maxMem = a[1]; + realTime = a[2] - b[2]; + cpu = a[3] - b[3]; + buffers = a[4]; + reads = a[5] - b[5]; + writes = a[6] - b[6]; + fetches = a[7] - b[7]; + } + }; + + IsqlStatist iStat(perf_before, perf_after); + const int TICK = +#ifndef WIN_NT + sysconf(_SC_CLK_TCK); +#else + 100; +#endif + // "%"SQUADFORMAT + // "%"SQUADFORMAT".%.2"SQUADFORMAT + IUTILS_printf2(Diag, diag->c_str(), iStat.curMem, iStat.deltaMem, iStat.maxMem, + iStat.realTime / TICK, (iStat.realTime % TICK) * 100 / TICK, + iStat.cpu / TICK, (iStat.cpu % TICK) * 100 / TICK, + iStat.buffers, iStat.reads, iStat.writes, iStat.fetches); + IUTILS_printf2(Diag, "%s", NEWLINE); - ISQL_FREE(input_sqlda); return CONT; } -// *********************************** -// p r i n t _ s q l d a _ o u t p u t -// *********************************** -// Show the contents of the output sqlda, that's the result of SELECT from -// both tables and procedures and execution of procedures that return output. -static void print_sqlda_output(const XSQLDA& sqlda) +// ********************************* +// p r i n t _ m e s s a g e +// ********************************* +// Show the contents of the input msg, used typically for parameters. +static void print_message(Firebird::IMessageMetadata* msg, const char* dir) { + unsigned n = msg->getCount(fbStatus); isqlGlob.printf( - //"\nOUTPUT SQLDA version: %d sqldaid: %s sqldabc: %ld sqln: %d sqld: %d\n", - "\nOUTPUT SQLDA version: %d sqln: %d sqld: %d\n", - //sqlda.version, sqlda.sqldaid, sqlda.sqldabc, sqlda.sqln, - sqlda.version, sqlda.sqln, - sqlda.sqld); - int i = 0; - const XSQLVAR* var = sqlda.sqlvar; - for (const XSQLVAR* const end = var + sqlda.sqld; var < end; var++, i++) + "\n%sPUT message field count: %d\n", dir, n); + for (unsigned i = 0; i < n; ++i) { - isqlGlob.printf("%02d: sqltype: %d %s %s sqlscale: %d sqlsubtype: %d sqllen: %d\n", - i + 1, var->sqltype, sqltype_to_string(var->sqltype), - (var->sqltype & 1) ? "Nullable" : " ", - var->sqlscale, var->sqlsubtype, var->sqllen); - isqlGlob.printf(" : name: (%d)%*s alias: (%d)%*s\n", - var->sqlname_length, var->sqlname_length, var->sqlname, - var->aliasname_length, var->aliasname_length, - var->aliasname); - isqlGlob.printf(" : table: (%d)%*s owner: (%d)%*s\n", - var->relname_length, var->relname_length, var->relname, - var->ownname_length, var->ownname_length, var->ownname); + unsigned type = msg->getType(fbStatus, i); + isqlGlob.printf("%02d: sqltype: %d %s %s scale: %d subtype: %d len: %d\n", + i + 1, type, sqltype_to_string(type), msg->isNullable(fbStatus, i) ? "Nullable" : " ", + msg->getScale(fbStatus, i), msg->getSubType(fbStatus, i), msg->getLength(fbStatus, i)); + isqlGlob.printf(" : name: %s alias: %s\n", + msg->getField(fbStatus, i), msg->getAlias(fbStatus, i)); + isqlGlob.printf(" : table: %s owner: %s\n", + msg->getRelation(fbStatus, i), msg->getOwner(fbStatus, i)); } } @@ -8010,26 +7810,34 @@ static void print_sqlda_output(const XSQLDA& sqlda) // *************************** // Write into buffers the information that will be printed as page header for // statements that return data. -static void process_header(const XSQLDA* sqlda, const unsigned pad[], TEXT header[], TEXT header2[]) +static void process_header(Firebird::IMessageMetadata* msg, const unsigned pad[], TEXT header[], TEXT header2[]) { // Create the column header : Left justify strings TEXT* p = header; TEXT* p2 = header2; - int i = 0; - const XSQLVAR* var = sqlda->sqlvar; - for (const XSQLVAR* const end = var + sqlda->sqld; var < end; var++, i++) + unsigned n_cols = msg->getCount(fbStatus); + if (ISQL_errmsg(fbStatus)) { - const SSHORT type = var->sqltype & ~1; + return; + } - IcuUtil::pad(p, isqlGlob.att_charset, var->aliasname_length, var->aliasname, pad[i], + for (unsigned i = 0; i < n_cols; ++i) + { + IsqlVar var; + if (ISQL_fill_var(&var, msg, i, NULL) == ps_ERR) + return; + + const unsigned type = var.type; + + IcuUtil::pad(p, isqlGlob.att_charset, strlen(var.alias), var.alias, pad[i], (type != SQL_TEXT && type != SQL_VARYING)); strcat(p, " "); p += strlen(p); // Separators need not go on forever no more than a line - unsigned limit = IcuUtil::charLength(isqlGlob.att_charset, var->aliasname_length, var->aliasname); + unsigned limit = IcuUtil::charLength(isqlGlob.att_charset, strlen(var.alias), var.alias); limit = MAX(limit, pad[i]) + 1; for (unsigned j = 1; j < limit && j < 80; j++) @@ -8048,23 +7856,25 @@ static void process_header(const XSQLDA* sqlda, const unsigned pad[], TEXT heade // any result to the caller. static void process_plan() { + if (!global_Stmt) + return; + // Bug 7565: A plan larger than plan_buffer will not be displayed // Bug 7565: Note also that the plan must fit into Print_Buffer - SCHAR plan_info[1]; + UCHAR plan_info[1]; plan_info[0] = ExplainPlan ? isc_info_sql_explain_plan : isc_info_sql_get_plan; - TEXT plan_buffer[PLAN_BUFFER_LENGTH]; + UCHAR plan_buffer[PLAN_BUFFER_LENGTH]; memset(plan_buffer, 0, sizeof(plan_buffer)); - char* planPtr = plan_buffer; + UCHAR* planPtr = plan_buffer; size_t planSize = sizeof(plan_buffer); - PtrSentry planSentry; + PtrSentry planSentry; for (int i = 0; i < 3; ++i) { - if (isc_dsql_sql_info(isc_status, &global_Stmt, sizeof(plan_info), plan_info, - planSize, planPtr)) + global_Stmt->getInfo(fbStatus, sizeof(plan_info), plan_info, planSize, planPtr); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); return; } @@ -8083,7 +7893,7 @@ static void process_plan() switch (i) { case 0: - planSentry.exchange((planPtr = new char[planSize = MAX_SSHORT]), true); + planSentry.exchange((planPtr = new UCHAR[planSize = MAX_SSHORT]), true); break; case 1: // Probably FB 2.1 and before will crash with MAX_USHORT. @@ -8091,7 +7901,7 @@ static void process_plan() isqlGlob.major_ods > 11) { planSentry.clean(); - planSentry.exchange((planPtr = new char[planSize = MAX_USHORT]), true); + planSentry.exchange((planPtr = new UCHAR[planSize = MAX_USHORT]), true); break; } // else fall into @@ -8122,6 +7932,8 @@ static void process_plan() // retrieving the information from the server. static SLONG process_record_count(const int statement_type) { + if (!global_Stmt) + return -1; UCHAR count_type = 0; // Skip selects, better to count records incoming later @@ -8140,12 +7952,12 @@ static SLONG process_record_count(const int statement_type) if (count_type) { - const SCHAR count_info[] = { isc_info_sql_records }; - SCHAR count_buffer[33]; - if (isc_dsql_sql_info(isc_status, &global_Stmt, sizeof(count_info), count_info, - sizeof(count_buffer), count_buffer)) + const UCHAR count_info[] = { isc_info_sql_records }; + UCHAR count_buffer[33]; + global_Stmt->getInfo(fbStatus, sizeof(count_info), count_info, + sizeof(count_buffer), count_buffer); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); return -1; } @@ -8175,14 +7987,12 @@ static SLONG process_record_count(const int statement_type) // A failure is indicated by returning zero. static SSHORT process_request_type() { - const SCHAR sqlda_info[] = { isc_info_sql_stmt_type }; - SCHAR info_buffer[16]; - if (isc_dsql_sql_info (isc_status, &global_Stmt, sizeof (sqlda_info), sqlda_info, - sizeof (info_buffer), info_buffer)) - { - ISQL_errmsg (isc_status); - } - else + if (!global_Stmt) + return 0; + const UCHAR sqlda_info[] = { isc_info_sql_stmt_type }; + UCHAR info_buffer[16]; + global_Stmt->getInfo(fbStatus, sizeof(sqlda_info), sqlda_info, sizeof(info_buffer), info_buffer); + if (!ISQL_errmsg(fbStatus)) { if (info_buffer[0] == isc_info_sql_stmt_type) { @@ -8198,84 +8008,30 @@ static SSHORT process_request_type() } -// *************************************** -// p r o c e s s _ s q l d a _ b u f f e r -// *************************************** -// Generate the buffer for sqlda output and assign the null indicators already created. -// The caller should deallocate the returned buffer after using it. -static SLONG* process_sqlda_buffer(XSQLDA* const sqlda, SSHORT nullind[]) -{ - SLONG bufsize = 0; - SSHORT* nullp = nullind; - XSQLVAR* var = sqlda->sqlvar; - for (const XSQLVAR* const end = var + sqlda->sqld; var < end; var++) - { - // Allocate enough space for all the sqldata and null pointers - - const SSHORT type = var->sqltype & ~1; - USHORT alignment, data_length; - alignment = data_length = var->sqllen; - - // special case db_key alignment which arrives as an SQL_TEXT - // Bug 8562: since DB_KEY arrives as SQL_TEXT, we need to account - // for the null character at the end of the string - if (!strncmp(var->sqlname, "DB_KEY", 6)) - { - alignment = 8; - // Room for null string terminator - data_length++; - } - else - { - // Special alignment cases - if (type == SQL_TEXT) - { - alignment = 1; - // Room for null string terminator - data_length++; - } - else if (type == SQL_VARYING) - { - alignment = sizeof(SSHORT); - // Room for a null string terminator for display - data_length += sizeof(SSHORT) + 1; - } - } - - bufsize = FB_ALIGN(bufsize, alignment) + data_length; - var->sqlind = nullp++; - } - - // Buffer for reading data from the fetch - if (bufsize) - { - SLONG* buffer = (SLONG*) ISQL_ALLOC ((SLONG) bufsize); - memset(buffer, 0, (size_t) bufsize); - return buffer; - } - return 0; -} - - -// ***************************************** -// p r o c e s s _ s q l d a _ d i s p l a y -// ***************************************** +// ********************************************* +// p r o c e s s _ m e s s a g e _ d i s p l a y +// ********************************************* // Calculate individual column widths and return total line width. -// Assign previously allocated buffer in pieces to every XSQLVAR. -static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], unsigned pad[]) +static unsigned process_message_display(Firebird::IMessageMetadata* message, unsigned pad[]) { - int linelength = 0; - SLONG offset = 0; - int i = 0; - XSQLVAR* var = sqlda->sqlvar; - for (const XSQLVAR* const end = var + sqlda->sqld; var < end; ++var, ++i) + unsigned linelength = 0; + unsigned ncols = message->getCount(fbStatus); + if (ISQL_errmsg(fbStatus)) { + return 0; + } + for (unsigned i = 0; i < ncols; ++i) + { + IsqlVar var; + if (ISQL_fill_var(&var, message, i, NULL) == ps_ERR) + return 0; + // Record the length of name and var, then convert to print // length, later to be stored in pad array - USHORT data_length, disp_length, alignment; - data_length = disp_length = alignment = var->sqllen; - SSHORT namelength = IcuUtil::charLength(isqlGlob.att_charset, var->aliasname_length, var->aliasname); + unsigned data_length, disp_length, alignment; + data_length = disp_length = alignment = var.length; + unsigned namelength = IcuUtil::charLength(isqlGlob.att_charset, strlen(var.alias), var.alias); // Minimum display length should not be less than that needed // for displaying null @@ -8283,7 +8039,7 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], unsigned if (namelength < NULL_DISP_LEN) namelength = NULL_DISP_LEN; - const SSHORT type = var->sqltype & ~1; + const unsigned type = var.type; switch (type) { @@ -8315,18 +8071,18 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], unsigned alignment = 1; data_length++; // OCTETS data is displayed in hex - if (var->sqlsubtype == 1) - disp_length = 2 * var->sqllen; - else if (var->sqlsubtype == 4) + if (var.charSet == 1) + disp_length = 2 * var.length; + else if (var.charSet == 4) disp_length /= 4; break; case SQL_VARYING: data_length += sizeof(USHORT) + 1; alignment = sizeof(USHORT); // OCTETS data is displayed in hex - if (var->sqlsubtype == 1) - disp_length = 2 * var->sqllen; - else if (var->sqlsubtype == 4) + if (var.charSet == 1) + disp_length = 2 * var.length; + else if (var.charSet == 4) disp_length /= 4; break; case SQL_SHORT: @@ -8348,10 +8104,10 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], unsigned // special case db_key alignment which arrives as an SQL_TEXT - if (!strncmp(var->sqlname, "DB_KEY", 6)) + if (!strncmp(var.field, "DB_KEY", 6)) { alignment = 8; - disp_length = 2 * var->sqllen; + disp_length = 2 * var.length; } // This is the print width of each column @@ -8364,29 +8120,23 @@ static SLONG process_sqlda_display(XSQLDA* const sqlda, SLONG buffer[], unsigned // Is there a collist entry, then use that width, but only for text if (type == SQL_TEXT || type == SQL_VARYING) { - if (!global_Cols.find(var->aliasname, &pad[i]) && global_Col_default) + if (!global_Cols.find(var.alias, &pad[i]) && global_Col_default) pad[i] = global_Col_default; disp_length = pad[i]; } - if ((type == SQL_TEXT || type == SQL_VARYING) && var->sqlsubtype == 4) + if ((type == SQL_TEXT || type == SQL_VARYING) && var.charSet == 4) disp_length *= 4; // The total line length linelength += disp_length + 1; - - // Allocate space in buffer for data - - offset = FB_ALIGN(offset, alignment); - var->sqldata = (SCHAR*) buffer + offset; - offset += data_length; } return linelength; } -static int process_statement(const TEXT* string, XSQLDA** sqldap) +static processing_state process_statement(const TEXT* str2) { /************************************** * @@ -8399,7 +8149,7 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) * This function uses the isc_dsql user functions rather * than embedded dynamic statements. The user request * is placed on transaction M__trans, while all - * background work is on the default gds_trans. + * background work is on the default fbTrans. * This function now returns CONT (success) or ps_ERR. **************************************/ @@ -8411,11 +8161,11 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) public: CancelHolder() { - fb_cancel_operation(isc_status, &DB, fb_cancel_enable); + DB->cancelOperation(fbStatus, fb_cancel_enable); } ~CancelHolder() { - fb_cancel_operation(isc_status, &DB, fb_cancel_disable); + DB->cancelOperation(fbStatus, fb_cancel_disable); } }; @@ -8424,45 +8174,36 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) // enable CANCEL during statement processing CancelHolder cHolder; - // We received the address of the sqlda in case we realloc it - XSQLDA* sqlda = *sqldap; - // If somebody did a commit or rollback, we are out of a transaction - if (!M__trans) - { - if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL)) - ISQL_errmsg(isc_status); - } + M_Transaction(); // No need to start a default transaction unless there is no current one - if (Autocommit && !D__trans) - { - if (isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb)) - { - ISQL_errmsg(isc_status); - } - } + if (Autocommit) + D_Transaction(); // If statistics are requested, then reserve them here - perf64 perf_before; + SINT64 perf_before[ISQL_COUNTERS]; if (Stats) - perf64_get_info(&DB, &perf_before); + { + Firebird::UtlInterfacePtr()->getPerfCounters(fbStatus, + DB, ISQL_COUNTERS_SET, perf_before); + if (ISQL_errmsg(fbStatus)) + { + return ps_ERR; + } + } // Prepare the dynamic query stored in string. // But put this on the DDL transaction to get maximum visibility of // metadata. - FB_API_HANDLE prepare_trans; - if (Autocommit) - prepare_trans = D__trans; - else - prepare_trans = M__trans; - - if (isc_dsql_prepare(isc_status, &prepare_trans, &global_Stmt, 0, string, - isqlGlob.SQL_dialect, sqlda)) + Firebird::ITransaction* prepare_trans = Autocommit ? D__trans : M__trans; + global_Stmt = DB->prepare(fbStatus, prepare_trans, 0, str2, isqlGlob.SQL_dialect, + Firebird::IStatement::PREPARE_PREFETCH_METADATA); + if (!fbStatus->isSuccess()) { if (isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION && Input_file) { @@ -8471,23 +8212,22 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) "**** Error preparing statement:", NEWLINE, NEWLINE, - string, + str2, NEWLINE); } - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; } // check for warnings - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); // Find out what kind of statement this is const int statement_type = process_request_type(); if (!statement_type) return ps_ERR; -//#ifdef DEV_BUILD if (Sqlda_display) { const bool can_have_input_parameters = @@ -8499,16 +8239,24 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) statement_type == isc_info_sql_stmt_select_for_upd || statement_type == isc_info_sql_stmt_get_segment; - if (print_sqlda_input(can_have_input_parameters) == ps_ERR) - return ps_ERR; + if (can_have_input_parameters) + { + Firebird::RefPtr + input(Firebird::REF_NO_INCR, global_Stmt->getInputMetadata(fbStatus)); + if (ISQL_errmsg(fbStatus)) + { + return ps_ERR; + } + + // check for warnings + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); + + print_message(input, "IN"); + } } -//#endif // DEV_BUILD - // check for warnings - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); - const bool is_selectable = statement_type == isc_info_sql_stmt_select || statement_type == isc_info_sql_stmt_select_for_upd || @@ -8535,30 +8283,29 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) if (Autocommit && (statement_type == isc_info_sql_stmt_ddl)) { - if (isc_dsql_execute_immediate(isc_status, &DB, &D__trans, 0, - string, isqlGlob.SQL_dialect, NULL)) + DB->execute(fbStatus, D__trans, 0, str2, isqlGlob.SQL_dialect, NULL, NULL, NULL, NULL); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); ret = ps_ERR; } - else if (isc_status[2] == isc_arg_warning) + else if (fbStatus->get()[2] == isc_arg_warning) { // check for warnings - ISQL_warning(isc_status); + ISQL_warning(fbStatus); } // CVC: DDL statements that aren't syntax errors are caught by DFW // only at commit time, so we need to check here. - // AP: isc_status will be cleaned in commit_trans() + // AP: fbStatus will be cleaned in commit_trans() if (!commit_trans(&D__trans)) ret = ps_ERR; // check for warnings from COMMIT - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); - if (Stats) - print_performance(&perf_before); + if (Stats && (print_performance(perf_before) == ps_ERR)) + ret = ps_ERR; return ret; } @@ -8569,27 +8316,27 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) // CVC: Starting a txn can fail, too. Let's check it, although I // suspect isql will catch it in frontend_set() through get_statement(), // so this place has little chance to be reached. - if (newtrans(string) == FAIL) + if (newtrans(str2) == FAIL) return ps_ERR; - if (Stats) - print_performance(&perf_before); + if (Stats && (print_performance(perf_before) == ps_ERR)) + ret = ps_ERR; return ret; } // This is a non-select DML statement or trans - if (isc_dsql_execute(isc_status, &M__trans, &global_Stmt, isqlGlob.SQL_dialect, NULL)) + M__trans = global_Stmt->execute(fbStatus, M__trans, NULL, NULL, NULL, NULL); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); // CVC: Make this conditional if it causes problems. For example // if (BailOnError) ret = ps_ERR; } // check for warnings - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); // We are executing a commit or rollback, commit default trans @@ -8615,76 +8362,53 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) } } - if (Stats) - print_performance(&perf_before); + if (Stats && (print_performance(perf_before) == ps_ERR)) + ret = ps_ERR; return ret; } - const SSHORT n_cols = sqlda->sqld; - - // The query is bigger than the curent sqlda, so make more room - - if (n_cols > sqlda->sqln) + Firebird::RefPtr + message(Firebird::REF_NO_INCR, global_Stmt->getOutputMetadata(fbStatus)); + if (ISQL_errmsg(fbStatus)) { - ISQL_FREE(sqlda); - sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(n_cols))); - memset(sqlda, 0, XSQLDA_LENGTH(n_cols)); - *sqldap = sqlda; - sqlda->version = SQLDA_VERSION1; - sqlda->sqld = sqlda->sqln = n_cols; - - // We must re-describe the sqlda, no need to re-prepare - - if (isc_dsql_describe(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda)) - { - ISQL_errmsg(isc_status); - return ps_ERR; - } + return ps_ERR; } -//#ifdef DEV_BUILD + // check for warnings + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); + + const unsigned n_cols = message->getCount(fbStatus); + if (ISQL_errmsg(fbStatus)) + { + return ps_ERR; + } // To facilitate debugging, the option // SET SQLDA_DISPLAY ON // will activate code to display the SQLDA after each statement. if (Sqlda_display) - print_sqlda_output(*sqlda); -//#endif // DEV_BUILD + print_message(message, "OUT"); + if (Planonly) return ret; - if (statement_type != isc_info_sql_stmt_exec_procedure) + unsigned bufLen = message->getMessageLength(fbStatus); + if (ISQL_errmsg(fbStatus)) { - // Otherwise, open the cursor to start things up - - if (isc_dsql_execute(isc_status, &M__trans, &global_Stmt, isqlGlob.SQL_dialect, NULL)) - { - ISQL_errmsg(isc_status); - return (ps_ERR); - } - - // check for warnings - if (isc_status[2] == isc_arg_warning) - ISQL_warning(isc_status); - + return ps_ERR; } - - SSHORT* nullind = NULL; - if (n_cols) { - nullind = (SSHORT*) ISQL_ALLOC((SLONG) (n_cols * sizeof(SSHORT))); - } - - SLONG* buffer = process_sqlda_buffer(sqlda, nullind); + UCHAR* buffer = global_Buffer->getBuffer(bufLen); // Pad is an array of lengths to be passed to the print_item unsigned* pad = NULL; - if (sqlda->sqld) { - pad = (unsigned*) ISQL_ALLOC ((SLONG) (sqlda->sqld * sizeof(int))); + if (n_cols) { + pad = (unsigned*) ISQL_ALLOC ((SLONG) (n_cols * sizeof(int))); } // Calculate display width and add a few for line termination, et al - const SLONG linelength = process_sqlda_display(sqlda, buffer, pad) + 10; + const SLONG linelength = process_message_display(message, pad) + 10; // Allocate the print line, the header line and the separator @@ -8699,22 +8423,21 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) *header = '\0'; *header2 = '\0'; - process_header(sqlda, pad, header, header2); + process_header(message, pad, header, header2); } // If this is an exec procedure, execute into the sqlda with one fetch only if (statement_type == isc_info_sql_stmt_exec_procedure) { - if (isc_dsql_execute2(isc_status, &M__trans, &global_Stmt, - isqlGlob.SQL_dialect, NULL, sqlda)) + global_Stmt->execute(fbStatus, M__trans, NULL, NULL, message, buffer); + if (ISQL_errmsg(fbStatus)) { - ISQL_errmsg(isc_status); ret = ps_ERR; } else { - if (sqlda->sqld) + if (n_cols) { // do not output unnecessary white text isqlGlob.printf(NEWLINE); @@ -8726,14 +8449,27 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) header2, NEWLINE); } - print_line(sqlda, pad, line); + print_line(message, buffer, pad, line); isqlGlob.printf(NEWLINE); } } } else { - // Otherwise fetch and print records until EOF + // Otherwise, open the cursor to start things up + + Firebird::IResultSet* curs = global_Stmt->openCursor(fbStatus, M__trans, + NULL, NULL, message); + if (ISQL_errmsg(fbStatus)) + { + return ps_ERR; + } + + // check for warnings + if (fbStatus->get()[2] == isc_arg_warning) + ISQL_warning(fbStatus); + + // Now fetch and print records until EOF #ifdef HAVE_TERMIOS_H int out_fd = fileno(isqlGlob.Out); @@ -8759,12 +8495,16 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) // Fetch the current cursor - if (isc_dsql_fetch(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda) == 100) - break; + if (!curs->fetchNext(fbStatus, buffer)) + { + if (fbStatus->isSuccess()) + { + break; + } + } // Print the header every Pagelength number of lines for - // command-line ISQL only. For WISQL, print the column - // headings only once. + // command-line ISQL only. if (printHead && (Pagelength && (lines % Pagelength == 0) || @@ -8778,40 +8518,16 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) NEWLINE); } - if (isc_status[1]) - { - // CVC: Add a \n so the error message is not at the right of - // header but in its own line, if no row was output yet. - if (!lines && !printHead) - isqlGlob.printf(NEWLINE); - - ISQL_errmsg(isc_status); - - // Avoid cancel during cleanup - fb_cancel_operation(isc_status, &DB, fb_cancel_disable); - - // CVC: It appears we can have an implicit cursor opened anyways. - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_close); - if (nullind) - ISQL_FREE(nullind); - if (pad) - ISQL_FREE(pad); - if (buffer) - ISQL_FREE(buffer); - if (header) - ISQL_FREE(header); - if (header2) - ISQL_FREE(header2); - if (line) - ISQL_FREE(line); - - return (ps_ERR); - } - if (!lines && !printHead) isqlGlob.printf(NEWLINE); - ret = print_line(sqlda, pad, line); + if (ISQL_errmsg(fbStatus)) + { + ret = ps_ERR; + break; + } + + ret = print_line(message, buffer, pad, line); } if (lines) @@ -8826,33 +8542,26 @@ static int process_statement(const TEXT* string, XSQLDA** sqldap) // Records affected: @1 isqlGlob.printf("%s%s", rec_count_msg, NEWLINE); } + + curs->close(fbStatus); } // Avoid cancel during cleanup - fb_cancel_operation(isc_status, &DB, fb_cancel_disable); - - // CVC: It appears we can have an implicit cursor opened anyways. - if (statement_type != isc_info_sql_stmt_exec_procedure) { - isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_close); - } + DB->cancelOperation(fbStatus, fb_cancel_disable); // Statistics printed here upon request - if (Stats) - print_performance(&perf_before); + if (Stats && (print_performance(perf_before) == ps_ERR)) + ret = ps_ERR; if (pad) ISQL_FREE(pad); - if (buffer) - ISQL_FREE(buffer); if (line) ISQL_FREE(line); if (header) ISQL_FREE(header); if (header2) ISQL_FREE(header2); - if (nullind) - ISQL_FREE(nullind); return (ret); } @@ -8890,8 +8599,9 @@ static int query_abort(const int reason, const int, void*) if (DB) { - ISC_STATUS_ARRAY status; - flag = (fb_cancel_operation(status, &DB, fb_cancel_raise) == FB_SUCCESS); + Firebird::LocalStatus status; + DB->cancelOperation(&status, fb_cancel_raise); + flag = status.isSuccess(); } if (flag) @@ -8961,8 +8671,7 @@ static void strip_quotes(const TEXT* in, TEXT* out) } -//#ifdef DEV_BUILD -static const char* sqltype_to_string(USHORT sqltype) +static const char* sqltype_to_string(unsigned sqltype) { /************************************** * @@ -8974,7 +8683,7 @@ static const char* sqltype_to_string(USHORT sqltype) * Return a more readable version of SQLDA.sqltype * **************************************/ - switch (sqltype & ~1) + switch (sqltype) { case SQL_TEXT: return "TEXT "; @@ -9012,4 +8721,3 @@ static const char* sqltype_to_string(USHORT sqltype) return "unknown "; } } -//#endif diff --git a/src/isql/isql.h b/src/isql/isql.h index 6145c6a5cf..0446b97f6f 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -94,12 +94,12 @@ enum LegacyTables ALL_objects }; -const size_t WORDLENGTH = 32; +const size_t WORDLENGTH = 32; // The worst case of a quoted identifier is 31 * 2 => 62 + 2 DQUOTES + TERM => 65. -const size_t QUOTEDLENGTH = 65; +const size_t QUOTEDLENGTH = 65; static const char* const DEFTERM = ";"; static const char* const DEFCHARSET = "NONE"; -const int NULL_DISP_LEN = 6; +const unsigned NULL_DISP_LEN = 6; // Error codes @@ -265,6 +265,9 @@ const int NO_GRANT_ON_CS = 177; // There is no privilege granted on character const int NO_GRANT_ON_COLL = 178; // There is no privilege granted on collation @1 in this database const int NO_GRANT_ON_PKG = 179; // There is no privilege granted on package @1 in this database const int NO_GRANT_ON_FUN = 180; // There is no privilege granted on function @1 in this database +const int REPORT_NEW1 = 181; // Current memory = !\nDelta memory = !\nMax memory = !\nElapsed time= ~ sec\n +const int REPORT_NEW2 = 182; // Cpu = ~ sec\n (skipped on windows) +const int REPORT_NEW3 = 183; // Buffers = !\nReads = !\nWrites = !\nFetches = !\n // Initialize types @@ -407,4 +410,33 @@ const char BLANK = '\040'; const char DBL_QUOTE = '\042'; const char SINGLE_QUOTE = '\''; +struct IsqlVar +{ + const char* field; + const char* relation; + const char* owner; + const char* alias; + int subType, scale; + unsigned type, length, charSet; + bool nullable; + short* nullInd; + union TypeMix + { + ISC_TIMESTAMP* asDateTime; + ISC_TIME* asTime; + ISC_DATE* asDate; + SSHORT* asSmallint; + SLONG* asInteger; + SINT64* asBigint; + float* asFloat; + double* asDouble; + FB_BOOLEAN* asBoolean; + ISC_QUAD* blobid; + vary* asVary; + char* asChar; + void* setPtr; + }; + TypeMix value; +}; + #endif // ISQL_ISQL_H diff --git a/src/isql/isql_proto.h b/src/isql/isql_proto.h index 26b306565f..02e772d30a 100644 --- a/src/isql/isql_proto.h +++ b/src/isql/isql_proto.h @@ -24,6 +24,10 @@ #ifndef ISQL_ISQL_PROTO_H #define ISQL_ISQL_PROTO_H +#include + +struct IsqlVar; + void ISQL_array_dimensions(const TEXT*); //void ISQL_build_table_list(void**, FILE*, FILE*, FILE*); //void ISQL_build_view_list(void**, FILE*, FILE*, FILE*); @@ -35,7 +39,7 @@ void ISQL_array_dimensions(const TEXT*); // SCHAR*, FILE*, FILE*, FILE*); bool ISQL_dbcheck(); void ISQL_disconnect_database(bool); -void ISQL_errmsg(const ISC_STATUS*); +bool ISQL_errmsg(Firebird::IStatus*); void ISQL_warning(ISC_STATUS*); void ISQL_exit_db(); // CVC: Not found. @@ -55,12 +59,13 @@ bool ISQL_is_domain(const TEXT*); #endif int ISQL_main(int, char**); bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int fieldScale); -void ISQL_print_validation(FILE*, ISC_QUAD*, bool, FB_API_HANDLE); +void ISQL_print_validation(FILE*, ISC_QUAD*, bool, Firebird::ITransaction*); //void ISQL_query_database(SSHORT*, FILE*, FILE*, FILE*); //void ISQL_reset_settings(); void ISQL_ri_action_print(const TEXT*, const TEXT*, bool); //int ISQL_sql_statement(TEXT*, FILE*, FILE*, FILE*); //void ISQL_win_err(const char*); -processing_state ISQL_print_item_blob(FILE*, const XSQLVAR*, FB_API_HANDLE, int subtype); +processing_state ISQL_print_item_blob(FILE*, const IsqlVar*, Firebird::ITransaction*, int subtype); +processing_state ISQL_fill_var(IsqlVar*, Firebird::IMessageMetadata*, unsigned, UCHAR*); #endif // ISQL_ISQL_PROTO_H diff --git a/src/isql/show.epp b/src/isql/show.epp index 2e92693e8c..3719ae4312 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -55,8 +55,11 @@ #include "../jrd/constants.h" #include "../common/classes/UserBlob.h" #include "../common/classes/VaryStr.h" +#include "../common/classes/ImplementHelper.h" #include "../isql/OptionsBase.h" +#include + #ifdef HAVE_CTYPE_H #include #endif @@ -74,7 +77,6 @@ DATABASE DB = EXTERN COMPILETIME "yachts.lnk"; enum commentMode {cmmShow, cmmExtract}; -static void local_fprintf(void*, const char*); static void remove_delimited_double_quotes(TEXT*); static void make_priv_string(USHORT, char*); static processing_state show_all_tables(SSHORT); @@ -111,6 +113,31 @@ static TEXT Print_buffer[512]; static TEXT SQL_identifier[BUFFER_LENGTH128]; static bool reReadDbOwner = true; + +namespace { + +// Used to make sure that local calls to print stuff go to isqlGlob.Out +// and not to stdout if IUtl::version gets called + +class VersionCallback : public Firebird::AutoIface +{ +public: + // IVersionCallback implementation + void FB_CARG callback(const char* text) + { + isqlGlob.printf("%s%s", text, NEWLINE); + } +}; + +} // anonymous namespace + + +static int ISQL_vax_integer(const UCHAR* bytes, USHORT length) +{ + return isc_vax_integer((const char*)bytes, length); +} + + // Initialize types // Keep this array in sync with obj.h in jrd. @@ -202,7 +229,7 @@ static const SCHAR db_dialect_info[] = // Added support to display FORCED WRITES status. - PR 27-NOV-2001 // Added support to display transaction info when next_transaction id is fixed. // Added support to display ODS version. CVC 26-Aug-2004. -static const SCHAR db_items[] = +static const UCHAR db_items[] = { isc_info_page_size, isc_info_db_size_in_pages, @@ -328,10 +355,10 @@ void SHOW_comments(bool force) } -bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, +bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle, SCHAR* info_buf, - const SCHAR* db_itemsL, - USHORT item_length, + const UCHAR* db_itemsL, + unsigned item_length, bool translate, const char* separator) { @@ -350,24 +377,22 @@ bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, * db_itemsL -- list of db_info items to process * **************************************/ - SCHAR buffer[BUFFER_LENGTH400]; + UCHAR buffer[BUFFER_LENGTH400]; TEXT msg[MSG_LENGTH]; - ISC_STATUS_ARRAY status_vector; - if (isc_database_info(status_vector, &db_handle, item_length, - db_itemsL, sizeof(buffer), buffer)) - { - ISQL_errmsg(status_vector); + Firebird::AutoPtr > + status_vector(fbMaster->getStatus()); + db_handle->getInfo(status_vector, item_length, db_itemsL, sizeof(buffer), buffer); + if (ISQL_errmsg(status_vector)) return false; - } *info_buf = '\0'; SCHAR* info = info_buf; - for (const SCHAR* d = buffer; *d != isc_info_end;) + for (const UCHAR* d = buffer; *d != isc_info_end;) { SLONG value_out = 0; - const SCHAR item = *d++; - const int length = isc_vax_integer(d, 2); + const UCHAR item = *d++; + const int length = ISQL_vax_integer(d, 2); d += 2; /* * This is not the best solution but it fixes the lack of characters @@ -381,12 +406,12 @@ bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, break; case isc_info_page_size: - value_out = isc_vax_integer(d, length); + value_out = ISQL_vax_integer(d, length); sprintf(info, "PAGE_SIZE %"SLONGFORMAT"%s", value_out, separator); break; case isc_info_db_size_in_pages: - value_out = isc_vax_integer(d, length); + value_out = ISQL_vax_integer(d, length); if (translate) { IUTILS_msg_get(NUMBER_PAGES, msg, SafeArg() << value_out); @@ -397,7 +422,7 @@ bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, break; case isc_info_sweep_interval: - value_out = isc_vax_integer(d, length); + value_out = ISQL_vax_integer(d, length); if (translate) { IUTILS_msg_get(SWEEP_INTERV, msg, SafeArg() << value_out); @@ -408,32 +433,32 @@ bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, break; case isc_info_forced_writes: - value_out = isc_vax_integer (d, length); + value_out = ISQL_vax_integer (d, length); sprintf (info, "Forced Writes are %s%s", (value_out == 1 ? "ON" : "OFF"), separator); break; case isc_info_oldest_transaction : - value_out = isc_vax_integer (d, length); + value_out = ISQL_vax_integer (d, length); sprintf(info, "Transaction - oldest = %"SLONGFORMAT"%s", value_out, separator); break; case isc_info_oldest_active : - value_out = isc_vax_integer (d, length); + value_out = ISQL_vax_integer (d, length); sprintf(info, "Transaction - oldest active = %"SLONGFORMAT"%s", value_out, separator); break; case isc_info_oldest_snapshot : - value_out = isc_vax_integer (d, length); + value_out = ISQL_vax_integer (d, length); sprintf(info, "Transaction - oldest snapshot = %"SLONGFORMAT"%s", value_out, separator); break; case isc_info_next_transaction : - value_out = isc_vax_integer (d, length); + value_out = ISQL_vax_integer (d, length); sprintf (info, "Transaction - Next = %"SLONGFORMAT"%s", value_out, separator); break; case isc_info_base_level: - value_out = isc_vax_integer(d, length); + value_out = ISQL_vax_integer(d, length); if (translate) { IUTILS_msg_get(BASE_LEVEL, msg, SafeArg() << value_out); @@ -444,7 +469,7 @@ bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, break; case isc_info_limbo: - value_out = isc_vax_integer(d, length); + value_out = ISQL_vax_integer(d, length); if (translate) { IUTILS_msg_get(LIMBO, msg, SafeArg() << value_out); @@ -455,10 +480,10 @@ bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, break; case isc_info_ods_version: - isqlGlob.major_ods = isc_vax_integer(d, length); + isqlGlob.major_ods = ISQL_vax_integer(d, length); break; case isc_info_ods_minor_version: - value_out = isc_vax_integer(d, length); + value_out = ISQL_vax_integer(d, length); sprintf(info, "ODS = %"SLONGFORMAT".%"SLONGFORMAT"%s", (SLONG) isqlGlob.major_ods, value_out, separator); break; @@ -468,7 +493,7 @@ bool SHOW_dbb_parameters(FB_API_HANDLE db_handle, { // Will print with garbage for now. //It's sprintf(info, "DB/Host = %.*s", length, d); - const UCHAR* s = reinterpret_cast(d); + const UCHAR* s = d; const UCHAR* end = s + length; ++s; // Skip useless indicator. int len = *s++; @@ -554,7 +579,7 @@ static const char* granted_by(char* buffer, const char* grantor) strcpy(owner, REL.RDB$OWNER_NAME); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); buffer[0] = '\0'; return ""; END_ERROR; @@ -815,7 +840,7 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; @@ -841,7 +866,7 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -912,12 +937,12 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -972,13 +997,13 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -1044,12 +1069,12 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -1120,12 +1145,12 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -1192,12 +1217,12 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -1264,12 +1289,12 @@ processing_state SHOW_grants2 (const SCHAR* object, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -1354,7 +1379,7 @@ void SHOW_grant_roles2 (const SCHAR* terminator, END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); END_ERROR; } @@ -1378,18 +1403,16 @@ void SHOW_print_metadata_text_blob(FILE* fp, ISC_QUAD* blobid, bool escape_squot if (UserBlob::blobIsNull(*blobid)) return; - UserBlob blob(isc_status); - if (!blob.open(DB, gds_trans, *blobid, sizeof(metadata_text_bpb), metadata_text_bpb)) - { - ISQL_errmsg(isc_status); + Firebird::IBlob* blob = DB->openBlob(fbStatus, fbTrans, blobid, + sizeof(metadata_text_bpb), metadata_text_bpb); + if (ISQL_errmsg(fbStatus)) return; - } SCHAR buffer[BUFFER_LENGTH512]; bool endedWithCr = false; - size_t length = 0; - while (blob.getSegment(sizeof(buffer) - 1, buffer, length)) + unsigned length; + while ((length = blob->getSegment(fbStatus, sizeof(buffer) - 1, buffer))) { // ASF: In Windows, \n characters are printed as \r\n in text mode. // If the original string has \r\n, they're printed as \r\r\n, resulting @@ -1431,8 +1454,8 @@ void SHOW_print_metadata_text_blob(FILE* fp, ISC_QUAD* blobid, bool escape_squot if (endedWithCr) fputc('\r', fp); - if (isc_status[1] && isc_status[1] != isc_segstr_eof) - ISQL_errmsg(isc_status); + if ((!fbStatus->isSuccess()) && fbStatus->get()[1] != isc_segstr_eof) + ISQL_errmsg(fbStatus); } @@ -1542,7 +1565,9 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) IUTILS_msg_get(VERSION, msg_string, SafeArg() << FB_VERSION); isqlGlob.printf("%s%s", msg_string, NEWLINE); isqlGlob.printf("Server version:%s", NEWLINE); - if (isc_version(&DB, local_fprintf, NULL)) + VersionCallback callback; + Firebird::UtlInterfacePtr()->version(fbStatus, DB, &callback); + if (!fbStatus->isSuccess()) { IUTILS_msg_get(CANNOT_GET_SRV_VER, msg_string); STDERROUT(msg_string); @@ -2129,24 +2154,6 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd) } -static void local_fprintf(void* /*format_ignored*/, const char* string) -{ -/************************************** - * - * l o c a l _ p r i n t f - * - ************************************** - * - * Functional description - * Used to make sure that local calls to print stuff go to isqlGlob.Out - * and not to stdout if isc_version gets called. - * - **************************************/ - - isqlGlob.printf("%s%s", string, NEWLINE); -} - - static void remove_delimited_double_quotes(TEXT* string) { /************************************** @@ -2220,7 +2227,7 @@ static processing_state show_all_tables(SSHORT sys_flag) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -2240,7 +2247,7 @@ static processing_state show_all_tables(SSHORT sys_flag) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -2379,7 +2386,7 @@ static processing_state show_check(const SCHAR* object) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -2469,7 +2476,7 @@ static processing_state show_collations(const SCHAR* object, SSHORT sys_flag, co } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -2564,7 +2571,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2579,7 +2586,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2605,13 +2612,13 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2637,13 +2644,13 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2670,13 +2677,13 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2690,7 +2697,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2710,7 +2717,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2724,7 +2731,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2738,7 +2745,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2754,7 +2761,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR } @@ -2769,7 +2776,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2785,7 +2792,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR } @@ -2802,7 +2809,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR } @@ -2817,7 +2824,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2831,7 +2838,7 @@ static processing_state show_comments(const commentMode showextract, const char* first = false; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -2864,7 +2871,7 @@ static void show_db() } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2930,7 +2937,7 @@ static void show_db() END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; @@ -2962,7 +2969,7 @@ static void show_db() isqlGlob.printf("%s", NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return; END_ERROR; } @@ -3025,7 +3032,7 @@ static processing_state show_dependencies(const char* object, int obj_type) } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -3068,7 +3075,7 @@ static processing_state show_dependencies(const char* object, int obj_type) } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -3081,7 +3088,7 @@ static processing_state show_dependencies(const char* object, int obj_type) is_table = true; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -3123,7 +3130,7 @@ static processing_state show_dependencies(const char* object, int obj_type) Object_types[obj_computed], DEP3.RDB$DEPENDED_ON_NAME, type_name); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -3165,7 +3172,7 @@ static processing_state show_dependencies(const char* object, int obj_type) Object_types[obj_computed], DEP3.RDB$DEPENDED_ON_NAME, type_name); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -3241,7 +3248,7 @@ static processing_state show_domains(const SCHAR* domain_name) odd = !odd; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (!first) @@ -3330,7 +3337,7 @@ static processing_state show_domains(const SCHAR* domain_name) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -3408,7 +3415,7 @@ static processing_state show_exceptions(const SCHAR* object) isqlGlob.printf("%s, %s%s", DEP.RDB$DEPENDENT_NAME, type, NEWLINE); END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; @@ -3422,7 +3429,7 @@ static processing_state show_exceptions(const SCHAR* object) isqlGlob.printf(NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -3458,7 +3465,7 @@ static processing_state show_filters(const SCHAR* object) odd = !odd; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (!first) @@ -3490,7 +3497,7 @@ static processing_state show_filters(const SCHAR* object) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -3531,7 +3538,7 @@ static processing_state show_sys_functions(const SCHAR* msg) odd = !odd; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -3614,7 +3621,7 @@ static processing_state show_functions(const SCHAR* funcname) Object_types[DEP.RDB$DEPENDED_ON_TYPE]); END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; } @@ -3623,7 +3630,7 @@ static processing_state show_functions(const SCHAR* funcname) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -3813,13 +3820,13 @@ static processing_state show_func(const SCHAR* funcname) END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -3934,7 +3941,7 @@ static processing_state show_func_legacy(const SCHAR* object) END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; } @@ -3943,7 +3950,7 @@ static processing_state show_func_legacy(const SCHAR* object) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -3969,11 +3976,33 @@ static processing_state show_generators(const SCHAR* object) SSHORT indicator; bool found = false; XSQLDA sqlda; - SINT64 genid64 = 0; - SLONG genid = 0; const char genIdStr[] = "SELECT GEN_ID(%s, 0) FROM RDB$DATABASE"; TEXT query[sizeof(genIdStr) + QUOTEDLENGTH], gen_name[QUOTEDLENGTH]; + Firebird::RefPtr + mb(Firebird::REF_NO_INCR, fbMaster->getMetadataBuilder(fbStatus, 1)); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + + // If the user has set his client dialect to 1, we take that to + // mean that he wants to see just the lower 32 bits of the + // generator, as in V5. Otherwise, we show him the whole 64-bit value. + bool use64 = isqlGlob.SQL_dialect >= SQL_DIALECT_V6_TRANSITION; + mb->setType(fbStatus, 0, use64 ? SQL_INT64 : SQL_LONG); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + + Firebird::RefPtr + outMetadata(Firebird::REF_NO_INCR, mb->getMetadata(fbStatus)); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + + unsigned char outBuffer[20]; + fb_assert(outMetadata->getMessageLength(fbStatus) <= sizeof(outBuffer)); + unsigned off = outMetadata->getOffset(fbStatus, 0); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + // Show all generators or named generator FOR GEN IN RDB$GENERATORS SORTED BY GEN.RDB$GENERATOR_NAME @@ -3995,56 +4024,30 @@ static processing_state show_generators(const SCHAR* object) sprintf (query, genIdStr, gen_name); - isc_stmt_handle stmt = 0; - isc_dsql_allocate_statement (isc_status, &DB, &stmt); - sqlda.sqln = 1; - sqlda.version = SQLDA_VERSION1; - - // If the user has set his client dialect to 1, we take that to - // mean that he wants to see just the lower 32 bits of the - // generator, as in V5. Otherwise, we show him the whole 64-bit value. - - if (isc_dsql_prepare (isc_status, &gds_trans, &stmt, 0, query, - isqlGlob.SQL_dialect, &sqlda)) - { - ISQL_errmsg (isc_status); + DB->execute(fbStatus, fbTrans, 0, query, isqlGlob.SQL_dialect, + NULL, NULL, outMetadata, outBuffer); + if (ISQL_errmsg (fbStatus)) continue; - } - if (isqlGlob.SQL_dialect >= SQL_DIALECT_V6_TRANSITION) - sqlda.sqlvar[0].sqldata = (SCHAR*) &genid64; - else - sqlda.sqlvar[0].sqldata = (SCHAR*) &genid; - sqlda.sqlvar[0].sqlind = &indicator; - // Singleton select needs no fetch - if (isc_dsql_execute2 (isc_status, &gds_trans, &stmt, isqlGlob.SQL_dialect, NULL, &sqlda)) + found = true; + if (isqlGlob.SQL_dialect >= SQL_DIALECT_V6_TRANSITION) { - ISQL_errmsg (isc_status); + isqlGlob.printf("Generator %s, current value is %" QUADFORMAT "d%s", + GEN.RDB$GENERATOR_NAME, + *((ISC_INT64*)&outBuffer[off]), + NEWLINE); } else { - found = true; - if (isqlGlob.SQL_dialect >= SQL_DIALECT_V6_TRANSITION) - { - isqlGlob.printf("Generator %s, current value is %" QUADFORMAT "d%s", - GEN.RDB$GENERATOR_NAME, - genid64, - NEWLINE); - } - else - { - isqlGlob.printf("Generator %s, current value is %ld%s", - GEN.RDB$GENERATOR_NAME, - genid, - NEWLINE); - } + isqlGlob.printf("Generator %s, current value is %ld%s", + GEN.RDB$GENERATOR_NAME, + *((SLONG*)&outBuffer[off]), + NEWLINE); } - if (isc_dsql_free_statement (isc_status, &stmt, DSQL_drop)) - ISQL_errmsg (isc_status); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (!found) @@ -4137,7 +4140,7 @@ static processing_state show_indices(const SCHAR* const* cmd) first = false; END_FOR - ON_ERROR ISQL_errmsg(isc_status); + ON_ERROR ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -4168,7 +4171,7 @@ static processing_state show_indices(const SCHAR* const* cmd) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -4207,7 +4210,7 @@ static processing_state show_packages(const SCHAR* package_name) odd = !odd; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (!first) @@ -4245,7 +4248,7 @@ static processing_state show_packages(const SCHAR* package_name) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -4335,7 +4338,7 @@ static processing_state show_proc(const SCHAR* procname) Object_types[DEP.RDB$DEPENDED_ON_TYPE]); END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; } @@ -4344,7 +4347,7 @@ static processing_state show_proc(const SCHAR* procname) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -4506,13 +4509,13 @@ static processing_state show_proc(const SCHAR* procname) END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -4553,7 +4556,7 @@ static processing_state show_role(const SCHAR* object) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -4597,7 +4600,7 @@ static processing_state show_role(const SCHAR* object) END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR @@ -4616,7 +4619,7 @@ static processing_state show_role(const SCHAR* object) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4641,9 +4644,9 @@ static processing_state show_secclass(const char* object, const char* opt) const bool detail = opt && (fb_utils::stricmp(opt, "DETAIL") == 0 || fb_utils::stricmp(opt, "DET") == 0); - XSQLVAR var; // To trick ISQL_print_item_blob() + IsqlVar var; memset(&var, 0, sizeof(var)); - var.sqlsubtype = isc_blob_acl; + var.subType = isc_blob_acl; int count = 0; @@ -4657,12 +4660,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.setPtr = &SC.RDB$ACL; + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4679,12 +4682,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4698,12 +4701,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4717,12 +4720,12 @@ static processing_state show_secclass(const char* object, const char* opt) SC.RDB$SECURITY_CLASS, NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4736,12 +4739,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4757,12 +4760,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4775,12 +4778,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4793,12 +4796,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -4811,12 +4814,12 @@ static processing_state show_secclass(const char* object, const char* opt) fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE); if (detail) { - var.sqldata = reinterpret_cast(&SC.RDB$ACL); - ISQL_print_item_blob(isqlGlob.Out, &var, gds_trans, isc_blob_acl); + var.value.asChar = reinterpret_cast(&SC.RDB$ACL); + ISQL_print_item_blob(isqlGlob.Out, &var, fbTrans, isc_blob_acl); } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR } @@ -4861,7 +4864,7 @@ static processing_state show_table(const SCHAR* relation_name, bool isView) first = true; END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -4994,7 +4997,7 @@ static processing_state show_table(const SCHAR* relation_name, bool isView) END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -5012,7 +5015,7 @@ static processing_state show_table(const SCHAR* relation_name, bool isView) isqlGlob.printf(NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -5076,7 +5079,7 @@ static processing_state show_table(const SCHAR* relation_name, bool isView) END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR; } @@ -5102,14 +5105,14 @@ static processing_state show_table(const SCHAR* relation_name, bool isView) isqlGlob.prints(NEWLINE); END_FOR ON_ERROR - ISQL_errmsg (isc_status); + ISQL_errmsg (fbStatus); return ps_ERR; END_ERROR } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR @@ -5128,7 +5131,7 @@ static processing_state show_table(const SCHAR* relation_name, bool isView) } END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -5195,7 +5198,7 @@ static processing_state show_trigger(const SCHAR* object, bool show_source, bool NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; } @@ -5235,7 +5238,7 @@ static processing_state show_trigger(const SCHAR* object, bool show_source, bool NEWLINE); END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; @@ -5275,7 +5278,7 @@ static processing_state show_trigger(const SCHAR* object, bool show_source, bool // skip = true; //END_FOR //ON_ERROR - // ISQL_errmsg (isc_status); + // ISQL_errmsg (fbStatus); // return ps_ERR; //END_ERROR; @@ -5313,7 +5316,7 @@ static processing_state show_trigger(const SCHAR* object, bool show_source, bool END_FOR ON_ERROR - ISQL_errmsg(isc_status); + ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; if (first) @@ -5335,7 +5338,7 @@ static processing_state show_users() * **************************************/ - const SCHAR user_items[] = + const UCHAR user_items[] = { isc_info_user_names, isc_info_end @@ -5343,51 +5346,41 @@ static processing_state show_users() TEXT msg[MSG_LENGTH]; // When messages can be translated. - Firebird::VaryStr my_user; - XSQLDA osqlda; // XSQLDA comes with one XSQLVAR inside. - memset(&osqlda, 0, XSQLDA_LENGTH(1)); - osqlda.version = SQLDA_VERSION1; - osqlda.sqln = 1; - osqlda.sqld = 1; - XSQLVAR& ovar = osqlda.sqlvar[0]; - ovar.sqlind = 0; - ovar.sqldata = reinterpret_cast(&my_user); - ovar.sqltype = SQL_VARYING; - ovar.sqllen = sizeof(my_user) - 3; + Firebird::RefPtr + mb(Firebird::REF_NO_INCR, fbMaster->getMetadataBuilder(fbStatus, 1)); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + mb->setType(fbStatus, 0, SQL_VARYING); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + mb->setLength(fbStatus, 0, MAX_SQL_IDENTIFIER_SIZE * 4); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + + Firebird::RefPtr + outMetadata(Firebird::REF_NO_INCR, mb->getMetadata(fbStatus)); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + + unsigned char outBuffer[MAX_SQL_IDENTIFIER_SIZE * 4 + 32]; + fb_assert(outMetadata->getMessageLength(fbStatus) <= sizeof(outBuffer)); + unsigned off = outMetadata->getOffset(fbStatus, 0); + if (ISQL_errmsg (fbStatus)) + return ps_ERR; + vary* my_user = (vary*)&outBuffer[off]; + const char* getuser = isqlGlob.major_ods < ODS_VERSION10 ? "select user from rdb$database" : "select current_user from rdb$database"; - -/* -#ifdef TRUSTED_AUTH - //if (Trusted_auth) - { - if (isqlGlob.major_ods > ODS_VERSION11 || - isqlGlob.major_ods == ODS_VERSION11 && isqlGlob.minor_ods > 1) - { - getuser = "select system_user from rdb$database"; - } - } -#endif -*/ - - if (isc_dsql_exec_immed2(isc_status, &DB, &gds_trans, 0, - getuser, - isqlGlob.SQL_dialect, NULL, &osqlda)) - { - ISQL_errmsg(isc_status); + DB->execute(fbStatus, fbTrans, 0, getuser, isqlGlob.SQL_dialect, + NULL, NULL, outMetadata, outBuffer); + if (ISQL_errmsg (fbStatus)) return OBJECT_NOT_FOUND; - } - my_user.vary_string[my_user.vary_length] = 0; // Just for the debugger. + my_user->vary_string[my_user->vary_length] = 0; // Just for the debugger. UCHAR buffer[BUFFER_LENGTH512]; - ISC_STATUS_ARRAY status_vector; - if (isc_database_info(status_vector, &DB, - sizeof(user_items), user_items, - sizeof(buffer), reinterpret_cast(buffer))) - { - ISQL_errmsg(status_vector); + DB->getInfo(fbStatus, sizeof(user_items), user_items, sizeof(buffer), buffer); + if (ISQL_errmsg(fbStatus)) return ps_ERR; - } bool newline = false; processing_state rc = OBJECT_NOT_FOUND; @@ -5416,10 +5409,8 @@ static processing_state show_users() fb_assert(len == length - 1); const UCHAR* uname = data + 1; // Let's mark all attachments with our same user with a # prefix. - if (len == my_user.vary_length && !memcmp(my_user.vary_string, uname, len)) - isqlGlob.printf("# %-37.*s", len, uname); - else - isqlGlob.printf(" %-37.*s", len, uname); + bool same(len == my_user->vary_length && !memcmp(my_user->vary_string, uname, len)); + isqlGlob.printf("%c %-37.*s", same ? '#' : ' ', len, uname); if (newline) isqlGlob.printf("\n"); diff --git a/src/isql/show_proto.h b/src/isql/show_proto.h index c55b511da5..a94d6e4d3e 100644 --- a/src/isql/show_proto.h +++ b/src/isql/show_proto.h @@ -25,9 +25,10 @@ #define ISQL_SHOW_PROTO_H #include "../common/classes/fb_string.h" +#include void SHOW_comments(bool force); -bool SHOW_dbb_parameters (FB_API_HANDLE, SCHAR*, const SCHAR*, USHORT, bool, const char*); +bool SHOW_dbb_parameters (Firebird::IAttachment*, SCHAR*, const UCHAR*, unsigned, bool, const char*); processing_state SHOW_grants (const SCHAR*, const SCHAR*, USHORT); processing_state SHOW_grants2 (const SCHAR*, const SCHAR*, USHORT, const TEXT*, bool); void SHOW_grant_roles (const SCHAR*, bool*); diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index a81de95641..76d39fe41b 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -13,7 +13,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM ('2012-02-18 20:00:00', 'SQLERR', 13, 1033) ('1996-11-07 13:38:42', 'SQLWARN', 14, 613) ('2006-09-10 03:04:31', 'JRD_BUGCHK', 15, 307) -('2013-08-21 23:00:00', 'ISQL', 17, 181) +('2014-01-13 15:41:16', 'ISQL', 17, 184) ('2010-07-10 10:50:30', 'GSEC', 18, 105) ('2012-05-25 19:59:42', 'GSTAT', 21, 56) ('2013-12-19 17:31:31', 'FBSVCMGR', 22, 58) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index b74eca28b3..1c12b6c019 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -2936,6 +2936,17 @@ Fetches = !f', NULL, NULL); ('NO_GRANT_ON_COLL', 'SHOW_metadata', 'show.e', NULL, 17, 178, NULL, 'There is no privilege granted on collation @1 in this database', NULL, NULL); ('NO_GRANT_ON_PKG', 'SHOW_metadata', 'show.e', NULL, 17, 179, NULL, 'There is no privilege granted on package @1 in this database', NULL, NULL); ('NO_GRANT_ON_FUN', 'SHOW_metadata', 'show.e', NULL, 17, 180, NULL, 'There is no privilege granted on function @1 in this database', NULL, NULL); +('REPORT_NEW1', 'print_performance', 'isql.epp', 'Each of these 4 items is followed by a newline (''\n'').', 17, 181, NULL, 'Current memory = ! +Delta memory = ! +Max memory = ! +Elapsed time= ~ sec +', NULL, NULL); +('REPORT_NEW2', 'print_performance', 'isql.epp', 'Each of these 5 items is followed by a newline (''\n'').', 17, 182, NULL, 'Cpu = ~ sec +', NULL, NULL); +('REPORT_NEW3', 'print_performance', 'isql.epp', 'Each of these 5 items is followed by a newline (''\n'').', 17, 183, NULL, 'Buffers = ! +Reads = ! +Writes = ! +Fetches = !', NULL, NULL); -- GSEC ('GsecMsg1', 'get_line', 'gsec.e', NULL, 18, 1, NULL, 'GSEC>', NULL, NULL); ('GsecMsg2', 'printhelp', 'gsec.e', 'This message is used in the Help display. It should be the same as number 1 (but in lower case).', 18, 2, NULL, 'gsec', NULL, NULL); diff --git a/src/remote/client/BlrFromMessage.cpp b/src/remote/client/BlrFromMessage.cpp index 5c1d9c7639..547d67d36b 100644 --- a/src/remote/client/BlrFromMessage.cpp +++ b/src/remote/client/BlrFromMessage.cpp @@ -107,7 +107,7 @@ void BlrFromMessage::buildBlr(IMessageMetadata* metadata) checkStatus(&st); unsigned charSet = metadata->getCharSet(&st, i); checkStatus(&st); - unsigned subType = metadata->getSubType(&st, i); + int subType = metadata->getSubType(&st, i); checkStatus(&st); switch (dtype) diff --git a/src/yvalve/MasterImplementation.cpp b/src/yvalve/MasterImplementation.cpp index 8e97ac8366..9cfe059bb7 100644 --- a/src/yvalve/MasterImplementation.cpp +++ b/src/yvalve/MasterImplementation.cpp @@ -45,6 +45,7 @@ #include "../common/ThreadStart.h" #include "../common/utils_proto.h" #include "../jrd/ibase.h" +#include "../yvalve/utl_proto.h" using namespace Firebird; @@ -667,6 +668,21 @@ Mutex& pauseTimer() } // namespace Why +// +// Utl (misc calls) +// + +namespace Why { + + Firebird::IUtl* FB_CARG MasterImplementation::getUtlInterface() + { + extern UtlInterface utlInterface; // Implemented in utl.cpp + return &utlInterface; + } + +} // namespace Why + + // // get master // diff --git a/src/yvalve/MasterImplementation.h b/src/yvalve/MasterImplementation.h index 64deaf6242..9e874c1e31 100644 --- a/src/yvalve/MasterImplementation.h +++ b/src/yvalve/MasterImplementation.h @@ -74,6 +74,7 @@ namespace Why Firebird::IMetadataBuilder* FB_CARG getMetadataBuilder(Firebird::IStatus* status, unsigned fieldCount); Firebird::IDebug* FB_CARG getDebug(); int FB_CARG serverMode(int mode); + Firebird::IUtl* FB_CARG getUtlInterface(); }; void shutdownTimers(); diff --git a/src/yvalve/perf.cpp b/src/yvalve/perf.cpp index 3e6f03fe60..6fc911604a 100644 --- a/src/yvalve/perf.cpp +++ b/src/yvalve/perf.cpp @@ -34,7 +34,9 @@ #include "../yvalve/perf.h" #include "../yvalve/gds_proto.h" #include "../yvalve/perf_proto.h" +#include "../yvalve/utl_proto.h" #include "../common/gdsassert.h" +#include "../common/classes/fb_string.h" #if defined(TIME_WITH_SYS_TIME) #include @@ -49,8 +51,29 @@ #include #endif +template +static SINT64 get_parameter(const T** ptr) +{ +/************************************** + * + * g e t _ p a r a m e t e r + * + ************************************** + * + * Functional description + * Get a parameter (length is encoded), convert to internal form, + * and return. + * + **************************************/ + SSHORT l = *(*ptr)++; + l += (*(*ptr)++) << 8; + const SINT64 parameter = isc_portable_integer(reinterpret_cast(*ptr), l); + *ptr += l; + + return parameter; +} + -static SINT64 get_parameter(const SCHAR**); #ifndef HAVE_TIMES static void times(struct tms*); #endif @@ -334,28 +357,6 @@ static void perf_report(const P* before, const P* after, SCHAR* buffer, SSHORT* } -static SINT64 get_parameter(const SCHAR** ptr) -{ -/************************************** - * - * g e t _ p a r a m e t e r - * - ************************************** - * - * Functional description - * Get a parameter (length is encoded), convert to internal form, - * and return. - * - **************************************/ - SSHORT l = *(*ptr)++; - l += (*(*ptr)++) << 8; - const SINT64 parameter = isc_portable_integer(reinterpret_cast(*ptr), l); - *ptr += l; - - return parameter; -} - - #ifndef HAVE_TIMES static void times(struct tms* buffer) { @@ -408,3 +409,175 @@ void API_ROUTINE perf64_report(const PERF64* before, const PERF64* after, SCHAR* { perf_report(before, after, buffer, buf_len); } + +namespace { + +static const unsigned CNT_DB_INFO = 1; +static const unsigned CNT_TIMER = 2; +enum CntTimer {CNT_TIME_REAL, CNT_TIME_USER, CNT_TIME_SYSTEM}; + +struct KnownCounters +{ + const char* name; + unsigned type; + unsigned code; +}; + +#define TOTAL_COUNTERS 11 + +// we use case-insensitive names, here they are written with capital letters for human readability +KnownCounters knownCounters[TOTAL_COUNTERS] = { + {"RealTime", CNT_TIMER, CNT_TIME_REAL}, + {"UserTime", CNT_TIMER, CNT_TIME_USER}, + {"SystemTime", CNT_TIMER, CNT_TIME_SYSTEM}, + {"Fetches", CNT_DB_INFO, isc_info_fetches}, + {"Marks", CNT_DB_INFO, isc_info_marks}, + {"Reads", CNT_DB_INFO, isc_info_reads}, + {"Writes", CNT_DB_INFO, isc_info_writes}, + {"CurrentMemory", CNT_DB_INFO, isc_info_current_memory}, + {"MaxMemory", CNT_DB_INFO, isc_info_max_memory}, + {"Buffers", CNT_DB_INFO, isc_info_num_buffers}, + {"PageSize", CNT_DB_INFO, isc_info_page_size} +}; + +} // anonymous namespace + +void FB_CARG Why::UtlInterface::getPerfCounters(Firebird::IStatus* status, Firebird::IAttachment* att, + const char* countersSet, ISC_INT64* counters) +{ + try + { + // Parse countersSet + unsigned cntLink[TOTAL_COUNTERS]; + memset(cntLink, 0xFF, sizeof cntLink); + Firebird::string dupSet(countersSet); + char* set = dupSet.begin(); + char* save = NULL; + const char* delim = " \t,;"; + unsigned typeMask = 0; + unsigned n = 0; + UCHAR info[TOTAL_COUNTERS]; // will never use all, but do not care about few bytes + UCHAR* pinfo = info; + + for (char* nm = strtok_r(set, delim, &save); nm; nm = strtok_r(NULL, delim, &save)) + { + Firebird::NoCaseString name(nm); + for (unsigned i = 0; i < TOTAL_COUNTERS; ++i) + { + if (name == knownCounters[i].name) + { + if (cntLink[i] != ~0u) + { + (Firebird::Arg::Gds(isc_random) << "Duplicated name").raise(); //report name & position + } + cntLink[i] = n++; + typeMask |= knownCounters[i].type; + if (knownCounters[i].type == CNT_DB_INFO) + { + *pinfo++ = knownCounters[i].code; + } + goto found; + } + } + (Firebird::Arg::Gds(isc_random) << "Unknown name").raise(); //report name & position +found: ; + } + + // Force reset counters + memset(counters, 0, n * sizeof(ISC_INT64)); + + // Fill time counters + if (typeMask & CNT_TIMER) + { + struct tms tus; + clock_t tr = times(&tus); + for (unsigned i = 0; i < TOTAL_COUNTERS; ++i) + { + if (cntLink[i] == ~0u) + continue; + if (knownCounters[i].type == CNT_TIMER) + { + clock_t v = 0; + switch(knownCounters[i].code) + { + case CNT_TIME_REAL: + v = tr; + break; + case CNT_TIME_USER: + v = tus.tms_utime; + break; + case CNT_TIME_SYSTEM: + v = tus.tms_stime; + break; + default: + fb_assert(false); + break; + } + counters[cntLink[i]] = v; + } + } + } + + // Fill DB counters + if (typeMask & CNT_DB_INFO) + { + UCHAR buffer[BUFFER_LARGE]; + att->getInfo(status, pinfo - info, info, sizeof(buffer), buffer); + if (!status->isSuccess()) + { + return; + } + + const UCHAR* p = buffer; + while (true) + { + SINT64 v = 0; + UCHAR ipb = *p++; + switch (ipb) + { + case isc_info_reads: + case isc_info_writes: + case isc_info_marks: + case isc_info_fetches: + case isc_info_num_buffers: + case isc_info_page_size: + case isc_info_current_memory: + case isc_info_max_memory: + v = get_parameter(&p); + break; + + case isc_info_end: + goto parsed; + + case isc_info_error: + { + const SINT64 temp = isc_portable_integer(p, 2); + fb_assert(temp <= MAX_SSHORT); + p += temp + 2; + continue; + } + + default: + (Firebird::Arg::Gds(isc_random) << "Unknown info code").raise(); //report char code + } + + for (unsigned i = 0; i < TOTAL_COUNTERS; ++i) + { + if (knownCounters[i].type == CNT_DB_INFO && knownCounters[i].code == ipb) + { + if (cntLink[i] != ~0u) + { + counters[cntLink[i]] = v; + } + break; + } + } + } +parsed: ; + } + } + catch (const Firebird::Exception& ex) + { + ex.stuffException(status); + } +} diff --git a/src/yvalve/prepa_proto.h b/src/yvalve/prepa_proto.h index 1681f2e05b..1562bf617a 100644 --- a/src/yvalve/prepa_proto.h +++ b/src/yvalve/prepa_proto.h @@ -24,7 +24,15 @@ #ifndef DSQL_PREPA_PROTO_H #define DSQL_PREPA_PROTO_H -bool PREPARSE_execute(ISC_STATUS*, FB_API_HANDLE*, FB_API_HANDLE*, USHORT, const SCHAR*, - bool*, USHORT); +namespace Firebird { + class IStatus; +} + +namespace Why { + class YAttachment; +} + +bool PREPARSE_execute(Firebird::IStatus*, Why::YAttachment**, + USHORT, const SCHAR*, bool*, USHORT); #endif // DSQL_PREPA_PROTO_H diff --git a/src/yvalve/preparse.cpp b/src/yvalve/preparse.cpp index 8ba92f2f1c..2dbb111e39 100644 --- a/src/yvalve/preparse.cpp +++ b/src/yvalve/preparse.cpp @@ -25,14 +25,18 @@ #include "firebird.h" #include #include -#include "../jrd/ibase.h" +//#include "../jrd/ibase.h" #include "../dsql/chars.h" -#include "../dsql/sqlda.h" +//#include "../dsql/sqlda.h" #include "../yvalve/prepa_proto.h" #include "../yvalve/gds_proto.h" +#include "../yvalve/YObjects.h" #include "../common/classes/ClumpletWriter.h" +//#include "../common/classes/ImplementHelper.h" #include "../common/StatusArg.h" +#include + enum pp_vals { PP_CREATE = 0, PP_DATABASE = 1, @@ -50,9 +54,9 @@ enum pp_vals { const size_t MAX_TOKEN_SIZE = 1024; -static void generate_error(ISC_STATUS*, const Firebird::string&, SSHORT, SSHORT); +static void generate_error(Firebird::IStatus*, const Firebird::string&, SSHORT, SSHORT); static SSHORT get_next_token(const SCHAR**, const SCHAR*, Firebird::string&); -static SSHORT get_token(ISC_STATUS*, SSHORT, bool, const SCHAR**, const SCHAR* const, +static SSHORT get_token(Firebird::IStatus*, SSHORT, bool, const SCHAR**, const SCHAR* const, Firebird::string&); struct pp_table @@ -102,17 +106,15 @@ using namespace Firebird; @brief - @param user_status - @param db_handle - @param trans_handle + @param status + @param ptrAtt @param stmt_length @param stmt @param stmt_eaten @param dialect **/ -bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, - FB_API_HANDLE*, // trans_handle, +bool PREPARSE_execute(IStatus* status, Why::YAttachment** ptrAtt, USHORT stmt_length, const SCHAR* stmt, bool* stmt_eaten, USHORT dialect) { // no use creating separate pool for a couple of strings @@ -130,13 +132,13 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, const char* const stmt_end = stmt + stmt_length; string token; - if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) || + if (get_token(status, SYMBOL, false, &stmt, stmt_end, token) || token.length() != pp_symbols[PP_CREATE].length || token != pp_symbols[PP_CREATE].symbol) { return false; } - if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) || + if (get_token(status, SYMBOL, false, &stmt, stmt_end, token) || (token.length() != pp_symbols[PP_DATABASE].length && token.length() != pp_symbols[PP_SCHEMA].length) || (token != pp_symbols[PP_DATABASE].symbol && token != pp_symbols[PP_SCHEMA].symbol)) @@ -144,7 +146,7 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, return false; } - if (get_token(user_status, STRING, false, &stmt, stmt_end, token)) + if (get_token(status, STRING, false, &stmt, stmt_end, token)) { return true; } @@ -182,8 +184,8 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, { case PP_PAGE_SIZE: case PP_PAGESIZE: - if (get_token(user_status, '=', true, &stmt, stmt_end, token) || - get_token(user_status, NUMERIC, false, &stmt, stmt_end, token)) + if (get_token(status, '=', true, &stmt, stmt_end, token) || + get_token(status, NUMERIC, false, &stmt, stmt_end, token)) { get_out = true; break; @@ -194,7 +196,7 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, break; case PP_USER: - if (get_token(user_status, STRING, false, &stmt, stmt_end, token)) + if (get_token(status, STRING, false, &stmt, stmt_end, token)) { get_out = true; break; @@ -205,7 +207,7 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, break; case PP_PASSWORD: - if (get_token(user_status, STRING, false, &stmt, stmt_end, token)) + if (get_token(status, STRING, false, &stmt, stmt_end, token)) { get_out = true; break; @@ -216,10 +218,10 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, break; case PP_SET: - if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) || + if (get_token(status, SYMBOL, false, &stmt, stmt_end, token) || token.length() != pp_symbols[PP_NAMES].length || token != pp_symbols[PP_NAMES].symbol || - get_token(user_status, STRING, false, &stmt, stmt_end, token)) + get_token(status, STRING, false, &stmt, stmt_end, token)) { get_out = true; break; @@ -232,8 +234,8 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, case PP_LENGTH: // Skip a token for value - if (get_token(user_status, '=', true, &stmt, stmt_end, token) || - get_token(user_status, NUMERIC, false, &stmt, stmt_end, token)) + if (get_token(status, '=', true, &stmt, stmt_end, token) || + get_token(status, NUMERIC, false, &stmt, stmt_end, token)) { get_out = true; break; @@ -256,13 +258,13 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, } while (matched); - isc_create_database(user_status, 0, file_name.c_str(), db_handle, - dpb.getBufferLength(), reinterpret_cast(dpb.getBuffer()), - 0); + RefPtr dispatcher(new Why::Dispatcher); + *ptrAtt = dispatcher->createDatabase(status, file_name.c_str(), + dpb.getBufferLength(), dpb.getBuffer()); } catch (const Exception& ex) { - ex.stuff_exception(user_status); + ex.stuffException(status); return true; } @@ -276,27 +278,28 @@ bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle, @brief - @param user_status + @param status @param token @param error @param result **/ -static void generate_error(ISC_STATUS* user_status, const string& token, SSHORT error, SSHORT result) +static void generate_error(IStatus* status, const string& token, SSHORT error, SSHORT result) { string err_string; - user_status[0] = isc_arg_gds; - user_status[1] = isc_sqlerr; - user_status[2] = isc_arg_number; - user_status[3] = -104; - user_status[4] = isc_arg_gds; + ISC_STATUS_ARRAY temp_status; + temp_status[0] = isc_arg_gds; + temp_status[1] = isc_sqlerr; + temp_status[2] = isc_arg_number; + temp_status[3] = -104; + temp_status[4] = isc_arg_gds; switch (error) { case UNEXPECTED_END_OF_COMMAND: - user_status[5] = isc_command_end_err; - user_status[6] = isc_arg_end; + temp_status[5] = isc_command_end_err; + temp_status[6] = isc_arg_end; break; case UNEXPECTED_TOKEN: @@ -309,15 +312,17 @@ static void generate_error(ISC_STATUS* user_status, const string& token, SSHORT } else err_string = token; - user_status[5] = isc_token_err; - user_status[6] = isc_arg_gds; - user_status[7] = isc_random; - user_status[8] = isc_arg_string; - user_status[9] = (ISC_STATUS)(err_string.c_str()); - user_status[10] = isc_arg_end; - Firebird::makePermanentVector(user_status); + temp_status[5] = isc_token_err; + temp_status[6] = isc_arg_gds; + temp_status[7] = isc_random; + temp_status[8] = isc_arg_string; + temp_status[9] = (ISC_STATUS)(err_string.c_str()); + temp_status[10] = isc_arg_end; + Firebird::makePermanentVector(temp_status); break; } + + status->set(temp_status); } @@ -470,7 +475,7 @@ static SSHORT get_next_token(const SCHAR** stmt, const SCHAR* stmt_end, string& @param token **/ -static SSHORT get_token(ISC_STATUS* status, +static SSHORT get_token(IStatus* status, SSHORT token_type, bool optional, const SCHAR** stmt, diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index b9b25667e7..6e616a3cb3 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -46,7 +46,6 @@ #include #include "../jrd/license.h" #include -//#include "../common/classes/timestamp.h" #include "../common/gdsassert.h" #include "../jrd/ibase.h" @@ -54,6 +53,8 @@ #include "../jrd/event.h" #include "../yvalve/gds_proto.h" #include "../yvalve/utl_proto.h" +#include "../yvalve/why_proto.h" +#include "../yvalve/prepa_proto.h" #include "../jrd/constants.h" #include "../common/classes/ClumpletWriter.h" #include "../common/utils_proto.h" @@ -62,6 +63,8 @@ #include "../common/classes/DbImplementation.h" #include "../common/ThreadStart.h" #include "../common/isc_f_proto.h" +#include "../common/StatusHolder.h" +#include "../common/classes/ImplementHelper.h" #ifdef HAVE_UNISTD_H #include @@ -79,6 +82,9 @@ #include #endif + +using namespace Firebird; + // Bug 7119 - BLOB_load will open external file for read in BINARY mode. #ifdef WIN_NT @@ -102,11 +108,8 @@ const int BSTR_input = 0; const int BSTR_output = 1; const int BSTR_alloc = 2; -static int dump(ISC_QUAD*, FB_API_HANDLE, FB_API_HANDLE, FILE*); -static int edit(ISC_QUAD*, FB_API_HANDLE, FB_API_HANDLE, SSHORT, const SCHAR*); -static int get_ods_version(FB_API_HANDLE*, USHORT*, USHORT*); +static void get_ods_version(IStatus*, IAttachment*, USHORT*, USHORT*); static void isc_expand_dpb_internal(const UCHAR** dpb, SSHORT* dpb_size, ...); -static int load(ISC_QUAD*, FB_API_HANDLE, FB_API_HANDLE, FILE*); // Blob info stuff @@ -120,10 +123,10 @@ static const char blob_items[] = // gds__version stuff -static const char info[] = +static const unsigned char info[] = { isc_info_firebird_version, isc_info_implementation, fb_info_implementation, isc_info_end }; -static const char ods_info[] = +static const unsigned char ods_info[] = { isc_info_ods_version, isc_info_ods_minor_version, isc_info_end }; static const TEXT* const impl_class[] = @@ -144,6 +147,491 @@ static const TEXT* const impl_class[] = "super server" // 13 }; + +namespace { + +class VersionCallback : public AutoIface +{ +public: + VersionCallback(FPTR_VERSION_CALLBACK routine, void* user) + : func(routine), arg(user) + { } + + // IVersionCallback implementation + void FB_CARG callback(const char* text) + { + func(arg, text); + } + +private: + FPTR_VERSION_CALLBACK func; + void* arg; +}; + +void load(IStatus* status, ISC_QUAD* blobId, IAttachment* att, ITransaction* tra, FILE* file) +{ +/************************************** + * + * l o a d + * + ************************************** + * + * Functional description + * Load a blob from a file. + * + **************************************/ + LocalStatus temp; + + // Open the blob. If it failed, what the hell -- just return failure + IBlob *blob = att->createBlob(status, tra, blobId); + if (!status->isSuccess()) + { + return; + } + + // Copy data from file to blob. Make up boundaries at end of line. + TEXT buffer[512]; + TEXT* p = buffer; + const TEXT* const buffer_end = buffer + sizeof(buffer); + + for (;;) + { + const SSHORT c = fgetc(file); + if (feof(file)) + break; + *p++ = static_cast(c); + if ((c != '\n') && p < buffer_end) + continue; + const SSHORT l = p - buffer; + blob->putSegment(status, l, buffer); + if (!status->isSuccess()) + { + blob->close(&temp); + return; + } + p = buffer; + } + + const SSHORT l = p - buffer; + if (l != 0) + { + blob->putSegment(status, l, buffer); + } + + blob->close(&temp); + return; +} + +void dump(IStatus* status, ISC_QUAD* blobId, IAttachment* att, ITransaction* tra, FILE* file) +{ +/************************************** + * + * d u m p + * + ************************************** + * + * Functional description + * Dump a blob into a file. + * + **************************************/ + // Open the blob. If it failed, what the hell -- just return failure + + IBlob *blob = att->openBlob(status, tra, blobId); + if (!status->isSuccess()) + { + return; + } + + // Copy data from blob to scratch file + + SCHAR buffer[256]; + const SSHORT short_length = sizeof(buffer); + + for (;;) + { + USHORT l = 0; + l = blob->getSegment(status, short_length, buffer); + if (!status->isSuccess() && status->get()[1] != isc_segment) + { + if (status->get()[1] == isc_segstr_eof) + status->init(); + break; + } + + if (l) + FB_UNUSED(fwrite(buffer, 1, l, file)); + } + + // Close the blob + + LocalStatus temp; + blob->close(&temp); +} + + +FB_BOOLEAN edit(IStatus* status, ISC_QUAD* blob_id, IAttachment* att, ITransaction* tra, + int type, const char* field_name) +{ +/************************************** + * + * e d i t + * + ************************************** + * + * Functional description + * Open a blob, dump it to a file, allow the user to edit the + * window, and dump the data back into a blob. If the user + * bails out, return FALSE, otherwise return TRUE. + * + * If the field name coming in is too big, truncate it. + * + **************************************/ +#if (defined WIN_NT) + TEXT buffer[9]; +#else + TEXT buffer[25]; +#endif + + const TEXT* q = field_name; + if (!q) + q = "gds_edit"; + + TEXT* p; + for (p = buffer; *q && p < buffer + sizeof(buffer) - 1; q++) + { + if (*q == '$') + *p++ = '_'; + else + *p++ = LOWER7(*q); + } + *p = 0; + + // Moved this out of #ifndef mpexl to get mktemp/mkstemp to work for Linux + // This has been done in the inprise tree some days ago. + // Would have saved me a lot of time, if I had seen this earlier :-( + // FSG 15.Oct.2000 + PathName tmpf = TempFile::create(status, buffer); + if (!status->isSuccess()) + return FB_FALSE; + + FILE* file = fopen(tmpf.c_str(), FOPEN_WRITE_TYPE_TEXT); + if (!file) + { + unlink(tmpf.c_str()); + system_error::raise("fopen"); + } + + dump(status, blob_id, att, tra, file); + + if (!status->isSuccess() && status->get()[1] != isc_segstr_eof) + { + isc_print_status(status->get()); + fclose(file); + unlink(tmpf.c_str()); + return FB_FALSE; + } + + fclose(file); + + if (gds__edit(tmpf.c_str(), type)) + { + + if (!(file = fopen(tmpf.c_str(), FOPEN_READ_TYPE_TEXT))) + { + unlink(tmpf.c_str()); + system_error::raise("fopen"); + } + + load(status, blob_id, att, tra, file); + + fclose(file); + return status->isSuccess(); + } + + unlink(tmpf.c_str()); + return FB_FALSE; +} + +} // anonymous namespace + + +namespace Why { + +UtlInterface utlInterface; + +FB_BOOLEAN FB_CARG UtlInterface::editBlob(Firebird::IStatus* status, ISC_QUAD* blobId, + Firebird::IAttachment* att, Firebird::ITransaction* tra, const char* tempFile) +{ + FB_BOOLEAN rc = FB_FALSE; + + try + { + rc = edit(status, blobId, att, tra, TRUE, tempFile); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + + if (!status->isSuccess() && status->get()[1] != isc_segstr_eof) + { + isc_print_status(status->get()); + } + + return rc; +} + +void FB_CARG UtlInterface::dumpBlob(IStatus* status, ISC_QUAD* blobId, + IAttachment* att, ITransaction* tra, const char* file_name, FB_BOOLEAN txt) +{ + FILE* file = fopen(file_name, txt ? FOPEN_WRITE_TYPE_TEXT : FOPEN_WRITE_TYPE); + try + { + if (!file) + system_error::raise("fopen"); + + dump(status, blobId, att, tra, file); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + fclose(file); +} + +void FB_CARG UtlInterface::loadBlob(IStatus* status, ISC_QUAD* blobId, + IAttachment* att, ITransaction* tra, const char* file_name, FB_BOOLEAN txt) +{ +/************************************** + * + * l o a d B l o b + * + ************************************** + * + * Functional description + * Load a blob with the contents of a file. + * + **************************************/ + FILE* file = fopen(file_name, txt ? FOPEN_READ_TYPE_TEXT : FOPEN_READ_TYPE); + try + { + if (!file) + system_error::raise("fopen"); + + load(status, blobId, att, tra, file); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + fclose(file); +} + +void UtlInterface::version(IStatus* status, IAttachment* att, + IVersionCallback* callback) +{ +/************************************** + * + * g d s _ $ v e r s i o n + * + ************************************** + * + * Functional description + * Obtain and print information about a database. + * + **************************************/ + try + { + UCharBuffer buffer; + USHORT buf_len = 256; + UCHAR* buf = buffer.getBuffer(buf_len); + + const TEXT* versions = 0; + const TEXT* implementations = 0; + const UCHAR* dbis = NULL; + bool redo; + do { + att->getInfo(status, sizeof(info), info, buf_len, buf); + if (!status->isSuccess()) + { + return; + } + + const UCHAR* p = buf; + redo = false; + + while (!redo && *p != isc_info_end && p < buf + buf_len) + { + const UCHAR item = *p++; + const USHORT len = static_cast(gds__vax_integer(p, 2)); + p += 2; + switch (item) + { + case isc_info_firebird_version: + versions = (TEXT*) p; + break; + + case isc_info_implementation: + implementations = (TEXT*) p; + break; + + case fb_info_implementation: + dbis = p; + if (dbis[0] * 6 + 1 > len) + { + // fb_info_implementation value appears incorrect + dbis = NULL; + } + break; + + case isc_info_error: + // old server does not understand fb_info_implementation + break; + + case isc_info_truncated: + redo = true; + break; + + default: + (Arg::Gds(isc_random) << "Invalid info item").raise(); + } + p += len; + } + + // Our buffer wasn't large enough to hold all the information, + // make a larger one and try again. + if (redo) + { + buf_len += 1024; + buf = buffer.getBuffer(buf_len); + } + } while (redo); + + UCHAR count = MIN(*versions, *implementations); + ++versions; + ++implementations; + + UCHAR diCount = 0; + if (dbis) + { + diCount = *dbis++; + } + + string s; + + UCHAR diCurrent = 0; + for (UCHAR level = 0; level < count; ++level) + { + const USHORT implementation_nr = *implementations++; + const USHORT impl_class_nr = *implementations++; + const int l = *versions++; // it was UCHAR + const TEXT* implementation_string; + string dbi_string; + if (dbis && dbis[diCurrent * 6 + 5] == level) + { + dbi_string = DbImplementation::pick(&dbis[diCurrent * 6]).implementation(); + implementation_string = dbi_string.c_str(); + if (++diCurrent >= diCount) + { + dbis = NULL; + } + } + else + { + dbi_string = DbImplementation::fromBackwardCompatibleByte(implementation_nr).implementation(); + implementation_string = dbi_string.nullStr(); + if (!implementation_string) + { + implementation_string = "**unknown**"; + } + } + const TEXT* class_string; + if (impl_class_nr >= FB_NELEM(impl_class) || !(class_string = impl_class[impl_class_nr])) + { + class_string = "**unknown**"; + } + s.printf("%s (%s), version \"%.*s\"", implementation_string, class_string, l, versions); + + callback->callback(s.c_str()); + versions += l; + } + + USHORT ods_version, ods_minor_version; + get_ods_version(status, att, &ods_version, &ods_minor_version); + if (!status->isSuccess()) + return; + + s.printf("on disk structure version %d.%d", ods_version, ods_minor_version); + callback->callback(s.c_str()); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } +} + +YAttachment* FB_CARG UtlInterface::executeCreateDatabase( + Firebird::IStatus* status, unsigned stmtLength, const char* creatDBstatement, + unsigned dialect, FB_BOOLEAN* stmtIsCreateDb) +{ + try + { + bool stmtEaten; + YAttachment* att = NULL; + + if (stmtIsCreateDb) + *stmtIsCreateDb = FB_FALSE; + + if (!PREPARSE_execute(status, &att, stmtLength, creatDBstatement, &stmtEaten, dialect)) + return NULL; + + if (stmtIsCreateDb) + *stmtIsCreateDb = FB_TRUE; + + if (!status->isSuccess()) + return NULL; + + LocalStatus tempStatus; + ITransaction* crdbTrans = att->startTransaction(status, 0, NULL); + + if (!status->isSuccess()) + { + att->dropDatabase(&tempStatus); + return NULL; + } + + bool v3Error = false; + + if (!stmtEaten) + { + att->execute(status, crdbTrans, stmtLength, creatDBstatement, dialect, NULL, NULL, NULL, NULL); + if (!status->isSuccess()) + { + crdbTrans->rollback(&tempStatus); + att->dropDatabase(&tempStatus); + return NULL; + } + } + + crdbTrans->commit(status); + if (!status->isSuccess()) + { + crdbTrans->rollback(&tempStatus); + att->dropDatabase(&tempStatus); + return NULL; + } + + return att; + } + catch (const Exception& ex) + { + ex.stuffException(status); + } +} + +} // namespace Why + + #if (defined SOLARIS ) || (defined __cplusplus) extern "C" { #endif @@ -499,7 +987,7 @@ int API_ROUTINE gds__edit(const TEXT* file_name, USHORT /*type*/) * Edit a file. * **************************************/ - Firebird::string editor; + string editor; #ifndef WIN_NT if (!fb_utils::readenv("VISUAL", editor) && !fb_utils::readenv("EDITOR", editor)) @@ -868,7 +1356,7 @@ void API_ROUTINE isc_set_login(const UCHAR** dpb, SSHORT* dpb_size) // look for the environment variables - Firebird::string username, password; + string username, password; if (!fb_utils::readenv(ISC_USER, username) && !fb_utils::readenv(ISC_PASSWORD, password)) return; @@ -962,12 +1450,12 @@ void API_ROUTINE isc_set_single_user(const UCHAR** dpb, SSHORT* dpb_size, const } + static void print_version(void*, const char* version) { printf("\t%s\n", version); } - int API_ROUTINE isc_version(FB_API_HANDLE* handle, FPTR_VERSION_CALLBACK routine, void* user_arg) { /************************************** @@ -983,142 +1471,15 @@ int API_ROUTINE isc_version(FB_API_HANDLE* handle, FPTR_VERSION_CALLBACK routine if (!routine) routine = print_version; - UCHAR buffer[256]; - UCHAR* buf = buffer; - USHORT buf_len = sizeof(buffer); - - ISC_STATUS_ARRAY status_vector; - const TEXT* versions = 0; - const TEXT* implementations = 0; - const UCHAR* dbis = NULL; - bool redo; - do { - if (isc_database_info(status_vector, handle, sizeof(info), info, - buf_len, reinterpret_cast(buf))) - { - if (buf != buffer) - gds__free(buf); - return FB_FAILURE; - } - - const UCHAR* p = buf; - redo = false; - - while (!redo && *p != isc_info_end && p < buf + buf_len) - { - const UCHAR item = *p++; - const USHORT len = static_cast(gds__vax_integer(p, 2)); - p += 2; - switch (item) - { - case isc_info_firebird_version: - versions = (TEXT*) p; - break; - - case isc_info_implementation: - implementations = (TEXT*) p; - break; - - case fb_info_implementation: - dbis = p; - if (dbis[0] * 6 + 1 > len) - { - // fb_info_implementation value appears incorrect - dbis = NULL; - } - break; - - case isc_info_error: - // old server does not understand fb_info_implementation - break; - - case isc_info_truncated: - redo = true; - break; - - default: - if (buf != buffer) - gds__free(buf); - return FB_FAILURE; - } - p += len; - } - - // Our buffer wasn't large enough to hold all the information, - // make a larger one and try again. - if (redo) - { - if (buf != buffer) - gds__free(buf); - buf_len += 1024; - buf = (UCHAR*) gds__alloc((SLONG) (sizeof(UCHAR) * buf_len)); - // FREE: freed within this module - if (!buf) // NOMEM: - return FB_FAILURE; - } - } while (redo); - - UCHAR count = MIN(*versions, *implementations); - ++versions; - ++implementations; - - UCHAR diCount = 0; - if (dbis) - { - diCount = *dbis++; - } - - TEXT s[128]; - - UCHAR diCurrent = 0; - for (UCHAR level = 0; level < count; ++level) - { - const USHORT implementation_nr = *implementations++; - const USHORT impl_class_nr = *implementations++; - const int l = *versions++; // it was UCHAR - const TEXT* implementation_string; - Firebird::string dbi_string; - if (dbis && dbis[diCurrent * 6 + 5] == level) - { - dbi_string = Firebird::DbImplementation::pick(&dbis[diCurrent * 6]).implementation(); - implementation_string = dbi_string.c_str(); - if (++diCurrent >= diCount) - { - dbis = NULL; - } - } - else - { - dbi_string = Firebird::DbImplementation::fromBackwardCompatibleByte(implementation_nr).implementation(); - implementation_string = dbi_string.nullStr(); - if (!implementation_string) - { - implementation_string = "**unknown**"; - } - } - const TEXT* class_string; - if (impl_class_nr >= FB_NELEM(impl_class) || !(class_string = impl_class[impl_class_nr])) - { - class_string = "**unknown**"; - } - fb_utils::snprintf(s, sizeof(s), "%s (%s), version \"%.*s\"", - implementation_string, class_string, l, versions); - - (*routine)(user_arg, s); - versions += l; - } - - if (buf != buffer) - gds__free(buf); - - USHORT ods_version, ods_minor_version; - if (get_ods_version(handle, &ods_version, &ods_minor_version) == FB_FAILURE) + LocalStatus st; + RefPtr att(REF_NO_INCR, handleToIAttachment(&st, handle)); + if (!st.isSuccess()) return FB_FAILURE; - sprintf(s, "on disk structure version %d.%d", ods_version, ods_minor_version); - (*routine)(user_arg, s); + VersionCallback callback(routine, user_arg); + UtlInterfacePtr()->version(&st, att, &callback); - return FB_SUCCESS; + return st.isSuccess() ? FB_SUCCESS : FB_FAILURE; } @@ -1140,8 +1501,8 @@ void API_ROUTINE isc_format_implementation(USHORT impl_nr, **************************************/ if (ibuflen > 0) { - Firebird::string imp = - Firebird::DbImplementation::fromBackwardCompatibleByte(impl_nr).implementation(); + string imp = + DbImplementation::fromBackwardCompatibleByte(impl_nr).implementation(); imp.copyTo(ibuf, ibuflen); } @@ -1252,7 +1613,7 @@ int API_ROUTINE blob__display(SLONG blob_id[2], * PASCAL callable version of EDIT_blob. * **************************************/ - const Firebird::MetaName temp(field_name, *name_length); + const MetaName temp(field_name, *name_length); return BLOB_display(reinterpret_cast(blob_id), *database, *transaction, temp.c_str()); } @@ -1270,12 +1631,32 @@ int API_ROUTINE BLOB_display(ISC_QUAD* blob_id, ************************************** * * Functional description - * Open a blob, dump it to a file, allow the user to read the - * window. + * Open a blob, dump it to stdout * **************************************/ + LocalStatus st; + RefPtr att(REF_NO_INCR, handleToIAttachment(&st, &database)); + if (!st.isSuccess()) + return FB_FAILURE; + RefPtr tra(REF_NO_INCR, handleToITransaction(&st, &transaction)); + if (!st.isSuccess()) + return FB_FAILURE; - return dump(blob_id, database, transaction, stdout); + try + { + dump(&st, blob_id, att, tra, stdout); + } + catch (const Exception& ex) + { + ex.stuffException(&st); + } + + if (!st.isSuccess() && st.get()[1] != isc_segstr_eof) + { + isc_print_status(st.get()); + return FB_FAILURE; + } + return FB_SUCCESS; } @@ -1311,6 +1692,41 @@ int API_ROUTINE blob__dump(SLONG blob_id[2], } +static int any_text_dump(ISC_QUAD* blob_id, + FB_API_HANDLE database, + FB_API_HANDLE transaction, + const SCHAR* file_name, + FB_BOOLEAN txt) +{ +/************************************** + * + * a n y _ t e x t _ d u m p + * + ************************************** + * + * Functional description + * Dump a blob into a file. + * + **************************************/ + LocalStatus st; + RefPtr att(REF_NO_INCR, handleToIAttachment(&st, &database)); + if (!st.isSuccess()) + return FB_FAILURE; + RefPtr tra(REF_NO_INCR, handleToITransaction(&st, &transaction)); + if (!st.isSuccess()) + return FB_FAILURE; + + UtlInterfacePtr()->dumpBlob(&st, blob_id, att, tra, file_name, txt); + + if (!st.isSuccess() && st.get()[1] != isc_segstr_eof) + { + isc_print_status(st.get()); + return FB_FAILURE; + } + return FB_SUCCESS; +} + + int API_ROUTINE BLOB_text_dump(ISC_QUAD* blob_id, FB_API_HANDLE database, FB_API_HANDLE transaction, @@ -1327,18 +1743,7 @@ int API_ROUTINE BLOB_text_dump(ISC_QUAD* blob_id, * This call does CR/LF translation on NT. * **************************************/ - FILE* file = fopen(file_name, FOPEN_WRITE_TYPE_TEXT); - if (!file) - return FALSE; - - if (!dump(blob_id, database, transaction, file)) - { - fclose(file); - unlink(file_name); - return FALSE; - } - fclose(file); - return TRUE; + return any_text_dump(blob_id, database, transaction, file_name, FB_TRUE); } @@ -1357,18 +1762,7 @@ int API_ROUTINE BLOB_dump(ISC_QUAD* blob_id, * Dump a blob into a file. * **************************************/ - FILE* file = fopen(file_name, FOPEN_WRITE_TYPE); - if (!file) - return FALSE; - - if (!dump(blob_id, database, transaction, file)) - { - fclose(file); - unlink(file_name); - return FALSE; - } - fclose(file); - return TRUE; + return any_text_dump(blob_id, database, transaction, file_name, FB_FALSE); } @@ -1388,7 +1782,7 @@ int API_ROUTINE blob__edit(SLONG blob_id[2], * into an internal edit call. * **************************************/ - const Firebird::MetaName temp(field_name, *name_length); + const MetaName temp(field_name, *name_length); return BLOB_edit(reinterpret_cast(blob_id), *database, *transaction, temp.c_str()); } @@ -1412,7 +1806,21 @@ int API_ROUTINE BLOB_edit(ISC_QUAD* blob_id, * **************************************/ - return edit(blob_id, database, transaction, TRUE, field_name); + LocalStatus st; + RefPtr att(REF_NO_INCR, handleToIAttachment(&st, &database)); + if (!st.isSuccess()) + return FB_FAILURE; + RefPtr tra(REF_NO_INCR, handleToITransaction(&st, &transaction)); + if (!st.isSuccess()) + return FB_FAILURE; + + int rc = UtlInterfacePtr()->editBlob(&st, blob_id, att, tra, field_name); + if (!st.isSuccess() && st.get()[1] != isc_segstr_eof) + { + isc_print_status(st.get()); + } + + return rc; } @@ -1488,6 +1896,42 @@ int API_ROUTINE blob__load(SLONG blob_id[2], } +static int any_text_load(ISC_QUAD* blob_id, + FB_API_HANDLE database, + FB_API_HANDLE transaction, + const TEXT* file_name, + FB_BOOLEAN flag) +{ +/************************************** + * + * a n y _ t e x t _ l o a d + * + ************************************** + * + * Functional description + * Load a blob with the contents of a file. + * Return TRUE is successful. + * + **************************************/ + LocalStatus st; + RefPtr att(REF_NO_INCR, handleToIAttachment(&st, &database)); + if (!st.isSuccess()) + return FB_FAILURE; + RefPtr tra(REF_NO_INCR, handleToITransaction(&st, &transaction)); + if (!st.isSuccess()) + return FB_FAILURE; + + UtlInterfacePtr()->loadBlob(&st, blob_id, att, tra, file_name, flag); + + if (!st.isSuccess()) + { + isc_print_status(st.get()); + return FB_FAILURE; + } + return FB_SUCCESS; +} + + int API_ROUTINE BLOB_text_load(ISC_QUAD* blob_id, FB_API_HANDLE database, FB_API_HANDLE transaction, @@ -1505,15 +1949,7 @@ int API_ROUTINE BLOB_text_load(ISC_QUAD* blob_id, * Return TRUE is successful. * **************************************/ - FILE* file = fopen(file_name, FOPEN_READ_TYPE_TEXT); - if (!file) - return FALSE; - - const int ret = load(blob_id, database, transaction, file); - - fclose(file); - - return ret; + return any_text_load(blob_id, database, transaction, file_name, FB_TRUE); } @@ -1532,15 +1968,7 @@ int API_ROUTINE BLOB_load(ISC_QUAD* blob_id, * Load a blob with the contents of a file. Return TRUE is successful. * **************************************/ - FILE* file = fopen(file_name, FOPEN_READ_TYPE); - if (!file) - return FALSE; - - const int ret = load(blob_id, database, transaction, file); - - fclose(file); - - return ret; + return any_text_load(blob_id, database, transaction, file_name, FB_FALSE); } @@ -1717,7 +2145,7 @@ int API_ROUTINE gds__thread_start(FPTR_INT_VOID_PTR* entrypoint, { Thread::start((ThreadEntryPoint*) entrypoint, arg, priority, (Thread::Handle*) thd_id); } - catch (const Firebird::status_exception& status) + catch (const status_exception& status) { rc = status.value()[1]; } @@ -1729,153 +2157,8 @@ int API_ROUTINE gds__thread_start(FPTR_INT_VOID_PTR* entrypoint, #endif -static int dump(ISC_QUAD* blob_id, FB_API_HANDLE database, FB_API_HANDLE transaction, FILE* file) -{ -/************************************** - * - * d u m p - * - ************************************** - * - * Functional description - * Dump a blob into a file. - * - **************************************/ - // bpb is irrelevant, not used. - const USHORT bpb_length = 0; - const UCHAR* bpb = NULL; - - // Open the blob. If it failed, what the hell -- just return failure - - FB_API_HANDLE blob = 0; - ISC_STATUS_ARRAY status_vector; - if (isc_open_blob2(status_vector, &database, &transaction, &blob, blob_id, bpb_length, bpb)) - { - isc_print_status(status_vector); - return FALSE; - } - - // Copy data from blob to scratch file - - SCHAR buffer[256]; - const SSHORT short_length = sizeof(buffer); - - for (;;) - { - USHORT l = 0; - isc_get_segment(status_vector, &blob, &l, short_length, buffer); - if (status_vector[1] && status_vector[1] != isc_segment) - { - if (status_vector[1] != isc_segstr_eof) - isc_print_status(status_vector); - break; - } - /* - const TEXT* p = buffer; - if (l) - do { - fputc(*p++, file); - } while (--l); - */ - if (l) - FB_UNUSED(fwrite(buffer, 1, l, file)); - } - - // Close the blob - - isc_close_blob(status_vector, &blob); - - return TRUE; -} - - -static int edit(ISC_QUAD* blob_id, - FB_API_HANDLE database, - FB_API_HANDLE transaction, - SSHORT type, - const SCHAR* field_name) -{ -/************************************** - * - * e d i t - * - ************************************** - * - * Functional description - * Open a blob, dump it to a file, allow the user to edit the - * window, and dump the data back into a blob. If the user - * bails out, return FALSE, otherwise return TRUE. - * - * If the field name coming in is too big, truncate it. - * - **************************************/ -#if (defined WIN_NT) - TEXT buffer[9]; -#else - TEXT buffer[25]; -#endif - - const TEXT* q = field_name; - if (!q) - q = "gds_edit"; - - TEXT* p; - for (p = buffer; *q && p < buffer + sizeof(buffer) - 1; q++) - { - if (*q == '$') - *p++ = '_'; - else - *p++ = LOWER7(*q); - } - *p = 0; - - // Moved this out of #ifndef mpexl to get mktemp/mkstemp to work for Linux - // This has been done in the inprise tree some days ago. - // Would have saved me a lot of time, if I had seen this earlier :-( - // FSG 15.Oct.2000 - Firebird::PathName tmpf = Firebird::TempFile::create(buffer); - if (tmpf.empty()) { - return FALSE; - } - - FILE* file = fopen(tmpf.c_str(), FOPEN_WRITE_TYPE_TEXT); - if (!file) - { - unlink(tmpf.c_str()); - return FALSE; - } - - if (!dump(blob_id, database, transaction, file)) - { - fclose(file); - unlink(tmpf.c_str()); - return FALSE; - } - - fclose(file); - - if (type = gds__edit(tmpf.c_str(), type)) - { - - if (!(file = fopen(tmpf.c_str(), FOPEN_READ_TYPE_TEXT))) - { - unlink(tmpf.c_str()); - return FALSE; - } - - load(blob_id, database, transaction, file); - - fclose(file); - - } - - unlink(tmpf.c_str()); - - return type; -} - - -static int get_ods_version(FB_API_HANDLE* handle, USHORT* ods_version, USHORT* ods_minor_version) +static void get_ods_version(IStatus* status, IAttachment* att, + USHORT* ods_version, USHORT* ods_minor_version) { /************************************** * @@ -1888,14 +2171,12 @@ static int get_ods_version(FB_API_HANDLE* handle, USHORT* ods_version, USHORT* o * of the database. * **************************************/ - ISC_STATUS_ARRAY status_vector; UCHAR buffer[16]; - isc_database_info(status_vector, handle, sizeof(ods_info), ods_info, - sizeof(buffer), reinterpret_cast(buffer)); + att->getInfo(status, sizeof(ods_info), ods_info, sizeof(buffer), buffer); - if (status_vector[1]) - return FB_FAILURE; + if (!status->isSuccess()) + return; const UCHAR* p = buffer; UCHAR item; @@ -1917,11 +2198,9 @@ static int get_ods_version(FB_API_HANDLE* handle, USHORT* ods_version, USHORT* o break; default: - return FB_FAILURE; + (Arg::Gds(isc_random) << "Invalid info item").raise(); } } - - return FB_SUCCESS; } @@ -2081,73 +2360,10 @@ static void isc_expand_dpb_internal(const UCHAR** dpb, SSHORT* dpb_size, ...) } -static int load(ISC_QUAD* blob_id, FB_API_HANDLE database, FB_API_HANDLE transaction, FILE* file) -{ -/************************************** - * - * l o a d - * - ************************************** - * - * Functional description - * Load a blob from a file. - * - **************************************/ - ISC_STATUS_ARRAY status_vector; - - // Open the blob. If it failed, what the hell -- just return failure - - FB_API_HANDLE blob = 0; - if (isc_create_blob(status_vector, &database, &transaction, &blob, blob_id)) - { - isc_print_status(status_vector); - return FALSE; - } - - // Copy data from file to blob. Make up boundaries at end of of line. - TEXT buffer[512]; - TEXT* p = buffer; - const TEXT* const buffer_end = buffer + sizeof(buffer); - - for (;;) - { - const SSHORT c = fgetc(file); - if (feof(file)) - break; - *p++ = static_cast(c); - if ((c != '\n') && p < buffer_end) - continue; - const SSHORT l = p - buffer; - if (isc_put_segment(status_vector, &blob, l, buffer)) - { - isc_print_status(status_vector); - isc_close_blob(status_vector, &blob); - return FALSE; - } - p = buffer; - } - - const SSHORT l = p - buffer; - if (l != 0) - { - if (isc_put_segment(status_vector, &blob, l, buffer)) - { - isc_print_status(status_vector); - isc_close_blob(status_vector, &blob); - return FALSE; - } - } - - isc_close_blob(status_vector, &blob); - - return TRUE; -} - - // new utl -static void setTag(Firebird::ClumpletWriter& dpb, UCHAR tag, const char* env, bool utf8) +static void setTag(ClumpletWriter& dpb, UCHAR tag, const char* env, bool utf8) { - Firebird::string value; + string value; if (fb_utils::readenv(env, value) && !dpb.find(tag)) { @@ -2158,7 +2374,7 @@ static void setTag(Firebird::ClumpletWriter& dpb, UCHAR tag, const char* env, bo } } -void setLogin(Firebird::ClumpletWriter& dpb, bool spbFlag) +void setLogin(ClumpletWriter& dpb, bool spbFlag) { const UCHAR address_path = spbFlag ? isc_spb_address_path : isc_dpb_address_path; const UCHAR trusted_auth = spbFlag ? isc_spb_trusted_auth : isc_dpb_trusted_auth; diff --git a/src/yvalve/utl_proto.h b/src/yvalve/utl_proto.h index 9766bb1b99..be3554afba 100644 --- a/src/yvalve/utl_proto.h +++ b/src/yvalve/utl_proto.h @@ -24,7 +24,11 @@ #ifndef JRD_UTL_PROTO_H #define JRD_UTL_PROTO_H +#include + #include "fb_types.h" +#include "../yvalve/YObjects.h" +#include "../common/classes/ImplementHelper.h" #ifdef __cplusplus extern "C" { @@ -78,5 +82,29 @@ namespace Firebird } void setLogin(Firebird::ClumpletWriter& dpb, bool spbFlag); +namespace Why +{ + +class UtlInterface : public Firebird::AutoIface +{ + // IUtl implementation +public: + void FB_CARG version(Firebird::IStatus* status, Firebird::IAttachment* att, + Firebird::IVersionCallback* callback); + void FB_CARG loadBlob(Firebird::IStatus* status, ISC_QUAD* blobId, Firebird::IAttachment* att, + Firebird::ITransaction* tra, const char* file, FB_BOOLEAN txt); + void FB_CARG dumpBlob(Firebird::IStatus* status, ISC_QUAD* blobId, Firebird::IAttachment* att, + Firebird::ITransaction* tra, const char* file, FB_BOOLEAN txt); + FB_BOOLEAN FB_CARG editBlob(Firebird::IStatus* status, ISC_QUAD* blobId, + Firebird::IAttachment* att, Firebird::ITransaction* tra, const char* tempFile = NULL); + void FB_CARG getPerfCounters(Firebird::IStatus* status, Firebird::IAttachment* att, + const char* countersSet, ISC_INT64* counters); // in perf.cpp + virtual YAttachment* FB_CARG executeCreateDatabase(Firebird::IStatus* status, + unsigned stmtLength, const char* creatDBstatement, unsigned dialect, + FB_BOOLEAN* stmtIsCreateDb = NULL); +}; + +} + #endif // JRD_UTL_PROTO_H diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index 90836310fc..344cb4b87a 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -62,7 +62,7 @@ #include "../common/classes/GetPlugins.h" #include "../common/classes/fb_tls.h" #include "../common/classes/InternalMessageBuffer.h" -#include "../yvalve/prepa_proto.h" +//#include "../yvalve/prepa_proto.h" #include "../yvalve/utl_proto.h" #include "../yvalve/why_proto.h" #include "../yvalve/MasterImplementation.h" @@ -85,7 +85,7 @@ using namespace Why; static void badHandle(ISC_STATUS code); static bool isNetworkError(const IStatus* status); static void nullCheck(const FB_API_HANDLE* ptr, ISC_STATUS code); -static void saveErrorString(ISC_STATUS* status); +//static void saveErrorString(ISC_STATUS* status); static void error804(ISC_STATUS err); static void sqldaDescribeParameters(XSQLDA* sqlda, const IMessageMetadata* parameters); static void sqldaMove(const XSQLDA* sqlda, Array& message, bool binding); @@ -103,6 +103,7 @@ static const USHORT DESCRIBE_BUFFER_SIZE = 1024; // size of buffer used in isc_d namespace Why { class StatusVector; + extern UtlInterface utlInterface; }; namespace { @@ -909,6 +910,7 @@ static void nullCheck(const FB_API_HANDLE* ptr, ISC_STATUS code) // This is need because there are cases where the memory allocated for strings in the status vector // is freed prior to surfacing them to the user. This is an attempt to save off 1 string to surface // to the user. Any other strings will be set to a standard string. +/* static void saveErrorString(ISC_STATUS* status) { const int MAXERRORSTRINGLENGTH = 250; @@ -974,7 +976,7 @@ static void saveErrorString(ISC_STATUS* status) } } } - + */ // Raises a DSQL -804 error message. static void error804(ISC_STATUS err) { @@ -1357,6 +1359,43 @@ ISC_STATUS API_ROUTINE fb_database_crypt_callback(ISC_STATUS* userStatus, ICrypt //------------------------------------- +Firebird::IAttachment* handleToIAttachment(Firebird::IStatus* status, FB_API_HANDLE* handle) +{ + try + { + RefPtr attachment(translateHandle(attachments, handle)); + attachment->addRef(); + return attachment; + } + catch (const Exception& e) + { + e.stuffException(status); + } + + return NULL; +} + + +Firebird::ITransaction* handleToITransaction(Firebird::IStatus* status, FB_API_HANDLE* handle) +{ + try + { + RefPtr transaction(translateHandle(transactions, handle)); + transaction->addRef(); + return transaction; + } + catch (const Exception& e) + { + e.stuffException(status); + } + + return NULL; +} + + +//------------------------------------- + + // Attach a database through the first subsystem that recognizes it. ISC_STATUS API_ROUTINE isc_attach_database(ISC_STATUS* userStatus, SSHORT fileLength, const TEXT* filename, FB_API_HANDLE* publicHandle, SSHORT dpbLength, const SCHAR* dpb) @@ -2151,69 +2190,13 @@ ISC_STATUS API_ROUTINE isc_dsql_exec_immed2_m(ISC_STATUS* userStatus, FB_API_HAN { StatusVector status(userStatus); - bool stmtEaten; + FB_BOOLEAN stmtIsCrDb = FB_FALSE; + YAttachment* att = utlInterface.executeCreateDatabase(&status, stmtLength, sqlStmt, dialect, &stmtIsCrDb); - if (PREPARSE_execute(status, dbHandle, traHandle, stmtLength, sqlStmt, &stmtEaten, dialect)) + if (stmtIsCrDb) { - if (status[1]) - return status[1]; - - ISC_STATUS_ARRAY tempStatus; - FB_API_HANDLE crdbTransHandle = 0; - - if (isc_start_transaction(status, &crdbTransHandle, 1, dbHandle, 0, 0)) - { - saveErrorString(status); - isc_drop_database(tempStatus, dbHandle); - *dbHandle = 0; - return status[1]; - } - - bool v3Error = false; - - if (!stmtEaten) - { - // Check if against < 4.0 database. - - const SCHAR ch = isc_info_base_level; - SCHAR buffer[16]; - if (!isc_database_info(status, dbHandle, 1, &ch, sizeof(buffer), buffer)) - { - if (buffer[0] != isc_info_base_level || buffer[4] > 3) - { - isc_dsql_exec_immed3_m(status, dbHandle, &crdbTransHandle, - stmtLength, sqlStmt, dialect, - inBlrLength, inBlr, inMsgType, inMsgLength, inMsg, - outBlrLength, outBlr, outMsgType, outMsgLength, outMsg); - } - else - v3Error = true; - } - } - - if (status[1]) - { - isc_rollback_transaction(tempStatus, &crdbTransHandle); - saveErrorString(status); - isc_drop_database(tempStatus, dbHandle); - *dbHandle = 0; - return status[1]; - } - - if (isc_commit_transaction(status, &crdbTransHandle)) - { - isc_rollback_transaction(tempStatus, &crdbTransHandle); - saveErrorString(status); - isc_drop_database(tempStatus, dbHandle); - *dbHandle = 0; - return status[1]; - } - - if (v3Error) - { - Arg::Gds(isc_srvr_version_too_old).copyTo(status); - return status[1]; - } + if (status.isSuccess()) + *dbHandle = att->getHandle(); return status[1]; } diff --git a/src/yvalve/why_proto.h b/src/yvalve/why_proto.h index f68b719328..89f6e2af69 100644 --- a/src/yvalve/why_proto.h +++ b/src/yvalve/why_proto.h @@ -27,6 +27,9 @@ namespace Firebird { class ICryptKeyCallback; + class IStatus; + class IAttachment; + class ITransaction; } extern "C" { @@ -263,4 +266,7 @@ ISC_STATUS API_ROUTINE gds__transaction_cleanup(ISC_STATUS*, FB_API_HANDLE*, } /* extern "C"*/ +Firebird::IAttachment* handleToIAttachment(Firebird::IStatus*, FB_API_HANDLE*); +Firebird::ITransaction* handleToITransaction(Firebird::IStatus*, FB_API_HANDLE*); + #endif // JRD_WHY_PROTO_H