8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 07:23:08 +01:00
firebird-mirror/src/isql/isql.epp

7045 lines
176 KiB
Plaintext
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: Interactive SQL utility
* MODULE: isql.epp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Main line routine
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
2001-05-23 15:26:42 +02:00
Revision 1.5 2000/11/18 16:49:24 fsg
Increased PRINT_BUFFER_LENGTH to 2048 to show larger plans
Fixed Bug #122563 in extract.e get_procedure_args
Apparently this has to be done in show.e also,
but that is for another day :-)
2001/05/20 Neil McCalden add planonly option
2002-06-29 15:39:11 +02:00
2001.09.09 Claudio Valderrama: put double quotes around identifiers
in dialect 3 only when needed. Solve mischievous declaration/invocation
of ISQL_copy_SQL_id that made no sense and caused pointer problems.
2001/10/03 Neil McCalden pick up Firebird version from database server
and display it with client version when -z used.
2001.10.09 Claudio Valderrama: try to disconnect gracefully in batch mode.
2001.11.23 Claudio Valderrama: skip any number of -- comments but only at
the beginning and ignore void statements like block comments followed by
a semicolon.
2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY)
2003-08-15 Fred Polizo, Jr. - Fixed print_item() to correctly print
string types as their hex represention for CHARACTER SET OCTETS.
2004-11-16 Damyan Ivanov - bail out on error in non-interactive mode.
2001-05-23 15:26:42 +02:00
*/
2001-12-24 03:51:06 +01:00
#ifdef DARWIN
#define _STLP_CCTYPE
#endif
#include "firebird.h"
2004-04-29 00:36:29 +02:00
#include <stdio.h>
#include "../dsql/keywords.h"
#include "../jrd/gds_proto.h"
2002-06-29 15:39:11 +02:00
#include <iostream>
2001-05-23 15:26:42 +02:00
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#include <ctype.h>
#include <errno.h>
2003-12-31 06:36:12 +01:00
#include "../common/utils_proto.h"
#include "../common/classes/ClumpletWriter.h"
2001-05-23 15:26:42 +02:00
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef WIN_NT
#include <io.h> // mktemp
#endif
#ifdef HAVE_EDITLINE_H
// This is a local file included in our distribution - but not always
// compiled into the system
#include "editline.h"
#endif
//#include <string>
2003-09-09 13:03:37 +02:00
enum literal_string_type
{
INIT_STR_FLAG = 0,
SINGLE_QUOTED_STRING = 1,
DOUBLE_QUOTED_STRING = 2,
NO_MORE_STRING = 3,
INCOMPLETE_STRING = 4
2003-09-09 13:03:37 +02:00
};
#include "../jrd/jrd_time.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/common.h"
2003-02-11 21:17:56 +01:00
#if defined(WIN_NT)
2001-05-23 15:26:42 +02:00
#include <windows.h>
#endif
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../isql/isql.h"
#include "../jrd/perf.h"
#include "../jrd/license.h"
#include "../jrd/constants.h"
#include "../jrd/ods.h"
2003-07-06 10:14:44 +02:00
#include "../jrd/file_params.h"
#include "../common/stuff.h"
2001-05-23 15:26:42 +02:00
#include "../isql/extra_proto.h"
#include "../isql/isql_proto.h"
#include "../isql/show_proto.h"
#include "../jrd/perf_proto.h"
#include "../jrd/utl_proto.h"
#include "../jrd/gdsassert.h"
DATABASE DB = COMPILETIME "yachts.lnk";
IsqlGlobals isqlGlob;
#define DIGIT(c) ((c) >= '0' && (c) <= '9')
#define INT64_LIMIT ((((SINT64) 1) << 62) / 5) // same as in cvt.cpp
2001-05-23 15:26:42 +02:00
// Print lengths of numeric values
2001-05-23 15:26:42 +02:00
2004-05-04 04:53:36 +02:00
const size_t MAXCHARSET_LENGTH = 32; // CHARSET names
const int SHORT_LEN = 7; // NUMERIC (4,2) = -327.68
const int LONG_LEN = 12; // NUMERIC (9,2) = -21474836.48
const int INT64_LEN = 21; // NUMERIC(18,2) = -92233720368547758.08
const int QUAD_LEN = 19;
const int FLOAT_LEN = 14; // -1.2345678E+38
const int DOUBLE_LEN = 23; // -1.234567890123456E+300
const int DATE_LEN = 11; // 11 for date only
const int DATETIME_LEN = 25; // 25 for date-time
const int TIME_ONLY_LEN = 13; // 13 for time only
const int DATE_ONLY_LEN = 11;
const int UNKNOWN_LEN = 20; // Unknown type: %d
const int MAX_TERMS = 10; // max # of terms in an interactive cmd
2004-05-09 07:48:33 +02:00
static inline void commit_trans(isc_tr_handle* x)
{
if (isc_commit_transaction (isc_status, x)) {
ISQL_errmsg (isc_status);
isc_rollback_transaction (isc_status, x);
}
}
2001-05-23 15:26:42 +02:00
static inline int fb_isspace(const char c)
{
return isspace((int)(UCHAR)c);
}
static inline int fb_isspace(const SSHORT c)
{
return isspace((int)(UCHAR)c);
}
2004-05-09 07:48:33 +02:00
2004-06-08 20:27:59 +02:00
struct indev {
2003-09-17 12:49:29 +02:00
indev* indev_next;
2004-04-29 00:36:29 +02:00
FILE* indev_fpointer;
2004-06-08 20:27:59 +02:00
};
typedef indev* INDEV;
2001-05-23 15:26:42 +02:00
2003-09-17 12:49:29 +02:00
struct collist {
collist* collist_next;
2001-05-23 15:26:42 +02:00
TEXT col_name[WORDLENGTH];
SSHORT col_len;
2003-09-17 12:49:29 +02:00
};
2001-05-23 15:26:42 +02:00
2003-09-17 12:49:29 +02:00
struct ri_actions {
const SCHAR* ri_action_name;
const SCHAR* ri_action_print_caps;
const SCHAR* ri_action_print_mixed;
2003-09-17 12:49:29 +02:00
};
2001-05-23 15:26:42 +02:00
static processing_state add_row(const TEXT*);
static processing_state blobedit(const TEXT*, const TEXT* const*);
static void col_check(const TEXT*, SSHORT*);
static void copy_str(TEXT**, const TEXT**, bool*, const TEXT* const,
const TEXT* const, literal_string_type);
static processing_state copy_table(const TEXT*, const TEXT*, const TEXT*);
static processing_state create_db(const TEXT*, const TEXT*);
static void do_isql();
static processing_state drop_db();
static processing_state edit(const TEXT* const*);
static processing_state end_trans();
static processing_state escape(const TEXT*);
static processing_state frontend(const TEXT*);
static void frontend_free_parms(TEXT*[], TEXT*[]);
static processing_state get_statement(TEXT* const, USHORT, const TEXT*);
static bool get_numeric(const UCHAR*, USHORT, SSHORT*, SINT64*);
static void get_str(const TEXT* const, const TEXT**, const TEXT**,
literal_string_type*);
static processing_state print_sets();
static processing_state help(const TEXT*);
static processing_state input(const TEXT*);
static bool isyesno(const TEXT*);
static processing_state newdb(TEXT*, const TEXT*, const TEXT*, const TEXT*, const TEXT*, bool);
static processing_state newoutput(const TEXT*);
static processing_state newsize(const TEXT*, const TEXT*);
static processing_state newtrans(const TEXT*);
2004-04-29 00:36:29 +02:00
static processing_state parse_arg(int, SCHAR**, SCHAR*, FILE**);
static SSHORT print_item(TEXT**, XSQLVAR*, SLONG);
2004-05-03 01:06:37 +02:00
static processing_state print_item_blob(FILE*, const XSQLVAR*, FB_API_HANDLE);
static void print_item_numeric(SINT64, SSHORT, SSHORT, TEXT*);
static processing_state print_line(XSQLDA*, const SLONG*, TEXT*);
static int process_statement(const TEXT*, XSQLDA**);
static void CLIB_ROUTINE query_abort(int);
2003-09-09 13:03:37 +02:00
static void strip_quotes(const TEXT*, TEXT*);
static processing_state do_set_command(const TEXT*, bool*);
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
static const char* sqltype_to_string(USHORT);
2001-05-23 15:26:42 +02:00
#endif
XSQLDA* global_sqlda;
XSQLDA** global_sqldap;
// The dialect spoken by the database, should be 0 when no database is connected.
2001-05-23 15:26:42 +02:00
USHORT dialect_spoken = 0;
USHORT requested_SQL_dialect = SQL_DIALECT_V6;
bool connecting_to_pre_v6_server = false;
SCHAR server_version[256];
bool Quiet;
bool Version_info = false;
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
bool fAnsiCP;
2001-05-23 15:26:42 +02:00
#endif
// Utility transaction handle
static isc_tr_handle D__trans;
static isc_tr_handle M__trans;
static SCHAR global_Numbufs[16]; /* # of cache buffers on connect */
static isc_stmt_handle global_Stmt;
static FILE* Ofp;
static FILE* Ifp;
static SCHAR Password[128];
static SCHAR Charset[128];
static bool Merge_stderr;
2004-08-27 07:00:31 +02:00
//static USHORT minor_ods; CVC: We do nothing useful with it!
static collist* global_Cols = NULL;
2001-05-23 15:26:42 +02:00
static SCHAR statistics[256];
static INDEV Filelist;
static TEXT Tmpfile[128];
2004-12-09 03:50:47 +01:00
static bool Abort_flag = false;
static bool Echo = false;
static bool Time_display = false;
static bool Sqlda_display = false;
static int Exit_value;
static bool Interactive = true;
static bool Input_file = false;
2001-05-23 15:26:42 +02:00
static SSHORT Pagelength = 20;
static bool Stats = false;
static bool Autocommit = true; // Commit ddl
static bool Warnings = true; // Print warnings
#ifdef SCROLLABLE_CURSORS
static bool Autofetch = true; // automatically fetch all records
2001-05-23 15:26:42 +02:00
static USHORT fetch_direction = 0;
static SLONG fetch_offset = 1;
#endif
static SSHORT Doblob = 1; // Default to printing only text types
static bool List = false;
static bool Docount = false;
static bool Plan = false;
static bool Planonly = false;
static bool Heading = true;
static bool BailOnError = false;
static int Termlen = 0;
2001-05-23 15:26:42 +02:00
static SCHAR ISQL_charset[MAXCHARSET_LENGTH] = { 0 };
static bool Merge_diagnostic = false;
2004-04-29 00:36:29 +02:00
static FILE* Diag;
static FILE* Help;
static const TEXT* const sql_prompt = "SQL> ";
#ifdef SCROLLABLE_CURSORS
static const TEXT* const fetch_prompt = "FETCH> ";
#endif
2001-05-23 15:26:42 +02:00
static bool global_psw = false;
static bool global_usr = false;
static bool global_role = false;
static bool has_global_numbufs = false;
static bool have_trans = false; // translation of word "Yes"
2001-05-23 15:26:42 +02:00
static TEXT yesword[MSG_LENGTH];
static TEXT Print_buffer[PRINT_BUFFER_LENGTH];
2003-12-31 06:36:12 +01:00
// Didn't replace it by FB_SHORT_MONTHS because these are uppercased.
static const SCHAR* alpha_months[] = {
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC"
};
#ifdef NOT_USED_OR_REPLACED
// There's a local version of this, more complete, in ISQL_get_version()
static const UCHAR db_version_info[] =
{
isc_info_ods_version,
2001-05-23 15:26:42 +02:00
isc_info_ods_minor_version,
isc_info_db_sql_dialect
};
#endif
#ifdef NOT_USED_OR_REPLACED
static const UCHAR text_bpb[] =
{
2001-05-23 15:26:42 +02:00
isc_bpb_version1,
isc_bpb_source_type, 1, isc_blob_text,
isc_bpb_target_type, 1, isc_blob_text
};
#endif
static UCHAR predefined_blob_subtype_bpb[] =
{
2001-05-23 15:26:42 +02:00
isc_bpb_version1,
isc_bpb_source_type, 1, 0,
isc_bpb_target_type, 1, isc_blob_text
};
// No check on input argument for now.
inline void set_bpb_for_translation(const unsigned int blob_sub_type)
{
predefined_blob_subtype_bpb[3] = (UCHAR) blob_sub_type;
}
2001-05-23 15:26:42 +02:00
// Note that these transaction options aren't understood in Version 3.3
static const UCHAR default_tpb[] =
{
isc_tpb_version1, isc_tpb_write,
2001-05-23 15:26:42 +02:00
isc_tpb_read_committed, isc_tpb_wait,
isc_tpb_no_rec_version
};
#ifdef NOT_USED_OR_REPLACED
// CVC: Just in case we need it for R/O operations in the future.
static const UCHAR batch_tpb[] =
{
isc_tpb_version3, isc_tpb_read,
isc_tpb_read_committed, isc_tpb_nowait,
isc_tpb_rec_version
2002-06-29 15:39:11 +02:00
};
#endif
2002-06-29 15:39:11 +02:00
// InterBase Performance Statistic format Variables
2001-05-23 15:26:42 +02:00
2003-09-17 12:49:29 +02:00
static perf perf_before, perf_after;
static bool have_report = false; // translation of report strings
2001-05-23 15:26:42 +02:00
static TEXT report_1[MSG_LENGTH + MSG_LENGTH];
#ifdef NOT_USED_OR_REPLACED
2001-05-23 15:26:42 +02:00
static TEXT report_2[MSG_LENGTH];
#endif
static TEXT rec_count_msg[MSG_LENGTH];
2001-05-23 15:26:42 +02:00
// if the action is restrict, do not print anything at all
2003-10-16 10:51:06 +02:00
static const ri_actions ri_actions_all[] =
{
{RI_ACTION_CASCADE, RI_ACTION_CASCADE, "Cascade"},
{RI_ACTION_NULL, RI_ACTION_NULL, "Set Null"},
{RI_ACTION_DEFAULT, RI_ACTION_DEFAULT, "Set Default"},
{RI_ACTION_NONE, RI_ACTION_NONE, "No Action"},
{RI_RESTRICT, "", ""},
{"", "", ""},
2003-10-16 10:51:06 +02:00
{0, 0, 0}
2001-05-23 15:26:42 +02:00
};
2003-07-06 10:14:44 +02:00
// We statically link ISQL against CLIB in MSVC6 and
// hence we've got problems sharing file handles with FBCLIENT.
// Since temp files are deleted by ISQL, let's create them here as well.
#if defined(_MSC_VER) && (_MSC_VER <= 1200)
2004-04-29 00:36:29 +02:00
static FILE* create_temp_file(const TEXT* string, TEXT* expanded_string)
2003-07-06 10:14:44 +02:00
{
gds__temp_dir(expanded_string);
strcat(expanded_string, string);
strcat(expanded_string, TEMP_PATTERN);
mktemp(expanded_string);
2004-04-29 00:36:29 +02:00
return fopen(expanded_string, "w+b");
2003-07-06 10:14:44 +02:00
}
#define gds__temp_file(flag, string, expanded_string) create_temp_file(string, expanded_string)
#endif
2001-05-23 15:26:42 +02:00
int CLIB_ROUTINE main(int argc,
char* argv[])
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m a i n
*
**************************************
*
* Functional description
* This calls ISQL_main, and exists to
* isolate main which does not exist under
* MS Windows.
*
**************************************/
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
2001-05-23 15:26:42 +02:00
{
const ULONG ulConsoleCP = GetConsoleCP();
2001-05-23 15:26:42 +02:00
if (ulConsoleCP == GetACP())
fAnsiCP = true;
2001-05-23 15:26:42 +02:00
else {
fAnsiCP = false;
2001-05-23 15:26:42 +02:00
if (ulConsoleCP != GetOEMCP() && Warnings) {
ISQL_printf(isqlGlob.Out,
"WARNING: The current codepage is not supported. Any use of any\n"
" extended characters may result in incorrect file names.\n");
2001-05-23 15:26:42 +02:00
}
}
}
#endif
// Initialize globals
isqlGlob.major_ods = 0;
2004-08-27 07:00:31 +02:00
//minor_ods = 0;
2001-05-23 15:26:42 +02:00
Exit_value = ISQL_main(argc, argv);
2001-12-24 03:51:06 +01:00
return Exit_value;
2001-05-23 15:26:42 +02:00
}
2003-12-03 09:19:24 +01:00
int ISQL_main(int argc,
char* argv[])
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ m a i n
*
**************************************
*
* Functional description
* Choose between reading and executing or generating SQL
* ISQL_main isolates this from main for PC Clients.
*
**************************************/
#ifdef MU_ISQL
/*
** This is code for QA Test bed Multiuser environment.
**
** Setup multi-user test environment.
*/
if (qa_mu_environment()) {
/*
** Initialize multi-user test manager only if in that
** environment.
*/
if (!qa_mu_init(argc, argv, 1)) {
/*
** Some problem in initializing multi-user test
** environment.
*/
qa_mu_cleanup();
exit(1);
}
else {
/*
** When invoked by MU, additional command-line argument
** was added at tail. ISQL is not interested in it so
** remove it.
*/
argv[argc - 1] = NULL;
argc--;
}
}
#endif // MU_ISQL
2001-05-23 15:26:42 +02:00
TEXT tabname[WORDLENGTH];
2001-05-23 15:26:42 +02:00
tabname[0] = '\0';
isqlGlob.db_SQL_dialect = 0;
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
// Output goes to stdout by default
isqlGlob.Out = stdout;
isqlGlob.Errfp = stderr;
2001-05-23 15:26:42 +02:00
processing_state ret = parse_arg(argc, argv, tabname, NULL);
2001-05-23 15:26:42 +02:00
// Detect if stdin is redirected
#ifdef WIN_NT
HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
2004-08-26 13:10:28 +02:00
if (GetFileType (in) == FILE_TYPE_DISK)
Interactive = false;
#else
if (!isatty(fileno(stdin)))
Interactive = false;
#endif
// Init the diagnostics and help files
if (Merge_diagnostic)
Diag = isqlGlob.Out;
else
2004-04-29 00:36:29 +02:00
Diag = stdout;
2004-04-29 00:36:29 +02:00
Help = stdout;
2001-05-23 15:26:42 +02:00
if (Merge_stderr)
isqlGlob.Errfp = isqlGlob.Out;
2001-05-23 15:26:42 +02:00
ISQL_make_upper(tabname);
2001-05-23 15:26:42 +02:00
switch (ret) {
case EXTRACT:
// This is a call to do extractions
if (*isqlGlob.global_Db_name) {
2001-05-23 15:26:42 +02:00
/* Let's use user and password if provided.
This should solve bug #112263 FSG 28.Jan.2001 */
newdb(isqlGlob.global_Db_name, isqlGlob.User, Password, global_Numbufs, isqlGlob.Role, false);
2001-05-23 15:26:42 +02:00
Exit_value = EXTRACT_ddl(SQL_objects, tabname);
ISQL_disconnect_database(true);
2002-06-29 15:39:11 +02:00
// isc_detach_database(isc_status, &DB);
2001-05-23 15:26:42 +02:00
}
break;
case EXTRACTALL:
if (*isqlGlob.global_Db_name) {
Interactive = false;
2001-05-23 15:26:42 +02:00
/* Let's use user and password if provided.
This should solve bug #112263 FSG 28.Jan.2001 */
newdb(isqlGlob.global_Db_name, isqlGlob.User, Password, global_Numbufs, isqlGlob.Role, false);
2002-06-29 15:39:11 +02:00
2001-05-23 15:26:42 +02:00
Exit_value = EXTRACT_ddl(ALL_objects, tabname);
2002-06-29 15:39:11 +02:00
ISQL_disconnect_database (true);
2002-06-29 15:39:11 +02:00
//isc_detach_database(isc_status, &DB);
2001-05-23 15:26:42 +02:00
}
break;
case ERR:
{
TEXT helpstring[155];
gds__msg_format(NULL, ISQL_MSG_FAC, USAGE1, sizeof(helpstring),
helpstring, NULL, NULL, NULL, NULL, NULL);
STDERROUT(helpstring, 1);
gds__msg_format(NULL, ISQL_MSG_FAC, USAGE2, sizeof(helpstring),
helpstring, NULL, NULL, NULL, NULL, NULL);
STDERROUT(helpstring, 1);
gds__msg_format(NULL, ISQL_MSG_FAC, USAGE3, sizeof(helpstring),
helpstring, NULL, NULL, NULL, NULL, NULL);
STDERROUT(helpstring, 1);
gds__msg_format(NULL, ISQL_MSG_FAC, USAGE4, sizeof(helpstring),
helpstring, NULL, NULL, NULL, NULL, NULL);
STDERROUT(helpstring, 1);
Exit_value = FINI_ERROR;
break;
}
2001-05-23 15:26:42 +02:00
default:
do_isql();
// keep Exit_value to whatever it is set
// by do_isql()
// Exit_value = FINI_OK;
2001-05-23 15:26:42 +02:00
break;
}
#ifdef MU_ISQL
/*
** This is code for QA Test bed Multiuser environment.
**
** Do multi-user cleanup if running in multi-user domain.
** qa_mu_cleanup() is NOP in non-multi-user domain.
*/
qa_mu_cleanup();
#endif // MU_ISQL
2001-05-23 15:26:42 +02:00
#ifdef DEBUG_GDS_ALLOC
/* As ISQL can run under windows, all memory should be freed before
* returning. In debug mode this call will report unfreed blocks.
*/
//gds_alloc_report(0, __FILE__, __LINE__);
char fn[] = __FILE__;
fn[strlen(fn) - 8] = 0; // all isql files in gen dir
gds_alloc_report(0, fn, 0);
2001-05-23 15:26:42 +02:00
#endif
return Exit_value;
}
void ISQL_array_dimensions(const TEXT* fieldname)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ a r r a y _ d i m e n s i o n s
*
**************************************
*
* Functional description
* Retrieves the dimensions of arrays and prints them.
*
* Parameters: fieldname -- the actual name of the array field
*
**************************************/
ISQL_printf(isqlGlob.Out, "[");
2001-05-23 15:26:42 +02:00
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return;
}
2001-05-23 15:26:42 +02:00
FOR FDIM IN RDB$FIELD_DIMENSIONS WITH
FDIM.RDB$FIELD_NAME EQ fieldname
SORTED BY FDIM.RDB$DIMENSION
/* Format is [lower:upper, lower:upper,..] */
if (FDIM.RDB$DIMENSION > 0) {
ISQL_printf (isqlGlob.Out, ", ");
}
sprintf (Print_buffer, "%ld:%ld", FDIM.RDB$LOWER_BOUND, FDIM.RDB$UPPER_BOUND);
ISQL_printf (isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return;
2001-05-23 15:26:42 +02:00
END_ERROR;
ISQL_printf(isqlGlob.Out, "] ");
2001-05-23 15:26:42 +02:00
}
// Same than fb_utils::exact_name, but writes the output to the second argument.
TEXT* ISQL_blankterm2(const TEXT* input, TEXT* output)
{
TEXT* q = output - 1;
for (TEXT* p = output; (*p = *input) != 0; ++p, ++input) {
if (*p != BLANK)
q = p;
}
*(q + 1) = 0;
return output;
}
2001-05-23 15:26:42 +02:00
bool ISQL_dbcheck()
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ d b c h e c k
*
**************************************
*
* Functional description
* Check to see if we are connected to a database.
* Return TRUE if connected, FALSE otherwise
*
**************************************/
TEXT errbuf[MSG_LENGTH];
if (!isqlGlob.global_Db_name[0]) {
2001-05-23 15:26:42 +02:00
if (!Quiet) {
gds__msg_format(NULL, ISQL_MSG_FAC, NO_DB, sizeof(errbuf),
errbuf, NULL, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
}
else
Exit_value = FINI_ERROR;
return false;
2001-05-23 15:26:42 +02:00
}
else
return true;
2001-05-23 15:26:42 +02:00
}
//static std::string userPrompt;
static char userPrompt2[MSG_LENGTH];
static const char* const userPrompt = userPrompt2;
static char* lastInputLine = NULL; // Apparently never deallocated when isql exits.
static int getColumn = -1;
2001-05-23 15:26:42 +02:00
void ISQL_prompt(const TEXT* string)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ p r o m p t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Print a prompt string for interactive user
* Not for Windows, otherwise flush the string
**************************************/
//userPrompt = (const char*) string;
strncpy(userPrompt2, string, MSG_LENGTH - 1);
userPrompt2[MSG_LENGTH - 1] = 0;
2002-06-29 15:39:11 +02:00
//#ifndef HAVE_READLINE_READLINE_H
2004-04-29 00:36:29 +02:00
// fprintf(stdout, userPrompt.c_str());
// fflush(stdout);
//#endif
2002-06-29 15:39:11 +02:00
}
static void readNextInputLine(const char* prompt)
{
/**************************************
*
* r e a d N e x t I n p u t L i n e
*
**************************************
*
* Functional description
* Get next input line and put it in lastInputLine
* If the first read is EOF set lineInputLine to NULL
* Otherwise return the line up to EOF (excluded) or '\n' (included)
**************************************/
while (true) {
if (lastInputLine != NULL) {
free(lastInputLine);
lastInputLine = NULL;
}
2002-06-29 15:39:11 +02:00
2004-04-29 00:36:29 +02:00
if (Ifp == stdin) {
#ifdef HAVE_EDITLINE_H
lastInputLine = readline(const_cast<char*>(prompt));
if (lastInputLine != NULL && strlen(lastInputLine) != 0) {
add_history(lastInputLine);
}
#else
// Write the prompt out.
2004-04-29 00:36:29 +02:00
fprintf(stdout, prompt);
fflush(stdout);
2004-12-09 02:53:31 +01:00
// Read the line
if (!feof(Ifp)) {
char buffer[MAX_USHORT];
fgets(buffer, sizeof(buffer), Ifp);
size_t lineSize = strlen(buffer);
lastInputLine = (char*) malloc(lineSize + 1);
2004-12-09 02:53:31 +01:00
memcpy(lastInputLine, buffer, lineSize + 1);
}
2004-12-09 02:53:31 +01:00
#endif
}
else {
// Write the prompt out, only if !Interactive && Echo
if (!Interactive && Echo) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, prompt);
fflush(stdout);
}
const int growStep = 1024;//128;
int size = 0;
int pos = 0;
while (true) {
2004-04-29 00:36:29 +02:00
int c = getc(Ifp);
if (c == 0x0D)
2004-04-29 00:36:29 +02:00
c = getc(Ifp);
if (c == EOF)
break;
if (pos >= size - 2) {
size += growStep;
char* line2 = (char*) malloc (size);
if (pos > 0) {
memcpy(line2, lastInputLine, pos + 1);
free(lastInputLine);
}
lastInputLine = line2;
}
if (c == '\n')
break;
else
lastInputLine[pos++] = static_cast<char>(c);
}
if (lastInputLine != NULL)
lastInputLine[pos] = '\0';
2004-12-09 02:53:31 +01:00
}
if (lastInputLine != NULL //&& strlen(lastInputLine) >= 2 redundant
&& lastInputLine[0] == '-' && lastInputLine[1] == '-')
{
if (Interactive || Echo) {
2004-04-29 00:36:29 +02:00
fprintf(stdout, "%s\n", lastInputLine);
fflush(stdout);
}
}
else
break;
}
getColumn = 0;
}
static void readNextInputLine()
{
readNextInputLine(userPrompt); //.c_str());
if (Echo && (lastInputLine != NULL)) {
ISQL_printf(isqlGlob.Out, lastInputLine);
ISQL_printf(isqlGlob.Out, NEWLINE);
}
}
int getNextInputChar()
{
/**************************************
*
* g e t N e x t I n p u t C h a r
*
**************************************
*
* Functional description
* Read next char from input discarding 0x0D characters
*
*
**************************************/
static int inputLen = 0;
// At end of line try and read next line
if (getColumn == -1) {
readNextInputLine();
if (lastInputLine)
inputLen = (int) strlen(lastInputLine);
}
// readline found EOF
if (lastInputLine == NULL) {
return EOF;
}
// If at end of line return \n
char c;
do {
if (getColumn == inputLen) //(int) strlen(lastInputLine))
{
getColumn = -1;
return '\n';
}
c = lastInputLine[getColumn++];
} while (c == 0x0D);
return c;
2001-05-23 15:26:42 +02:00
}
void ISQL_copy_SQL_id(const TEXT* in_str,
TEXT* output_str,
TEXT escape_char)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ c o p y _ S Q L _ i d
*
**************************************
*
* Functional description
*
* Copy/rebuild the SQL identifier by adding escape double quote if
* double quote is part of the SQL identifier and wraps around the
* SQL identifier with delimited double quotes
2001-05-23 15:26:42 +02:00
*
**************************************/
2002-06-29 15:39:11 +02:00
/* CVC: Try to detect if we can get rid of double quotes as
requested by Ann. Notice empty names need double quotes.
Assume the caller invoked previously ISQL_blankterm() that's
2003-12-31 06:36:12 +01:00
just another function like DYN_terminate, MET_exact_name, etc.
ISQL_blankterm has been replaced by fb_utils::exact_name. */
if (escape_char == DBL_QUOTE) {
// Cannot rely on ANSI functions that may be localized.
bool need_quotes = *in_str < 'A' || *in_str > 'Z';
TEXT* q1 = output_str;
for (const TEXT* p1 = in_str; *p1 && !need_quotes; ++p1, ++q1) {
if ((*p1 < 'A' || *p1 > 'Z') && (*p1 < '0' || *p1 > '9')
&& *p1 != '_' && *p1 != '$')
{
need_quotes = true;
}
*q1 = *p1;
}
if (!need_quotes) {
if (KEYWORD_stringIsAToken(in_str)) {
need_quotes = true;
}
if (!need_quotes) {
*q1 = '\0';
return;
}
}
}
TEXT* q1 = output_str;
*q1++ = escape_char;
for (const TEXT* p1 = in_str; *p1; p1++) {
*q1++ = *p1;
if (*p1 == escape_char) {
*q1++ = escape_char;
}
}
*q1++ = escape_char;
*q1 = '\0';
2001-05-23 15:26:42 +02:00
}
void ISQL_errmsg(const ISC_STATUS* status)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ e r r m s g
*
**************************************
*
* Functional description
* Report error conditions
2004-04-29 00:36:29 +02:00
* Simulate isc_print_status exactly, to control stderr
2001-05-23 15:26:42 +02:00
**************************************/
TEXT errbuf[MSG_LENGTH];
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
2001-05-23 15:26:42 +02:00
#define TRANSLATE_CP if (!fAnsiCP) AnsiToOem(errbuf, errbuf)
#else
#define TRANSLATE_CP
#endif
const ISC_STATUS* vec = status;
2001-05-23 15:26:42 +02:00
if (Quiet)
Exit_value = FINI_ERROR;
else {
if (vec[0] != isc_arg_gds ||
(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;
}
gds__msg_format(NULL, ISQL_MSG_FAC, 0, sizeof(errbuf), errbuf,
(TEXT*)(IPTR)isc_sqlcode(status), NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
TRANSLATE_CP;
STDERROUT(errbuf, 1);
if (fb_interpret(errbuf, sizeof(errbuf), &vec))
{
2001-05-23 15:26:42 +02:00
TRANSLATE_CP;
STDERROUT(errbuf, 1);
// Continuation of error
errbuf[0] = '-';
while (fb_interpret(errbuf + 1, sizeof(errbuf) - 1, &vec)) {
TRANSLATE_CP;
STDERROUT(errbuf, 1);
}
2001-05-23 15:26:42 +02:00
}
}
}
void ISQL_warning(ISC_STATUS* status)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ w a r n i n g
*
**************************************
*
* Functional desription
* Report warning
2004-04-29 00:36:29 +02:00
* Simulate isc_print_status exactly, to control stderr
2001-05-23 15:26:42 +02:00
**************************************/
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
2001-05-23 15:26:42 +02:00
#define TRANSLATE_CP if (!fAnsiCP) AnsiToOem(buf, buf)
#else
#define TRANSLATE_CP
#endif
const ISC_STATUS* vec = status;
fb_assert(vec[1] == 0); // shouldn't be any errors
2001-05-23 15:26:42 +02:00
if (!Quiet) {
if (vec[0] != isc_arg_gds ||
vec[2] != isc_arg_warning ||
(vec[2] == isc_arg_warning && !Warnings))
{
return;
}
TEXT buf[MSG_LENGTH];
if (fb_interpret(buf, sizeof(buf), &vec))
{
2001-05-23 15:26:42 +02:00
TRANSLATE_CP;
STDERROUT(buf, 1);
// Continuation of warning
buf[0] = '-';
while (fb_interpret(buf + 1, sizeof(buf) - 1, &vec)) {
TRANSLATE_CP;
STDERROUT(buf, 1);
}
2001-05-23 15:26:42 +02:00
}
}
status[2] = isc_arg_end;
2001-05-23 15:26:42 +02:00
}
SSHORT ISQL_get_default_char_set_id()
{
/*************************************
2001-05-23 15:26:42 +02:00
*
* I S Q L _ g e t _ d e f a u l t _ c h a r _ s e t _ i d
*
**************************************
*
* Functional description
* Return the database default character set
* id.
*
* -1 if the value can not be determined.
*
**************************************/
/* What is the default character set for this database?
There are three states:
1. There is no entry available in RDB$DATABASE
Then - NONE
2. The entry in RDB$DATABASE does not exist in
2001-05-23 15:26:42 +02:00
RDB$CHARACTER_SETS
Then - -1 to cause all character set defs to show
3. An entry in RDB$CHARACTER_SETS
Then - RDB$CHARACTER_SET_ID
*/
SSHORT default_char_set_id = 0;
2001-05-23 15:26:42 +02:00
FOR FIRST 1 EXT IN RDB$DATABASE
WITH EXT.RDB$CHARACTER_SET_NAME NOT MISSING;
default_char_set_id = -1;
FOR FIRST 1 CHI IN RDB$CHARACTER_SETS
WITH CHI.RDB$CHARACTER_SET_NAME = EXT.RDB$CHARACTER_SET_NAME
default_char_set_id = CHI.RDB$CHARACTER_SET_ID;
END_FOR;
2001-05-23 15:26:42 +02:00
END_FOR;
return (default_char_set_id);
}
SSHORT ISQL_get_field_length(const TEXT* field_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ g e t _ f i e l d _ l e n g t h
*
**************************************
*
* Retrieve character or field length of character types.
2001-05-23 15:26:42 +02:00
*
**************************************/
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return 0;
}
2001-05-23 15:26:42 +02:00
SSHORT l = 0;
FOR FLD IN RDB$FIELDS WITH
FLD.RDB$FIELD_NAME EQ field_name
if (FLD.RDB$CHARACTER_LENGTH.NULL)
l = FLD.RDB$FIELD_LENGTH;
else
l = FLD.RDB$CHARACTER_LENGTH;
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return 0;
END_ERROR;
2001-05-23 15:26:42 +02:00
return l;
}
void ISQL_get_character_sets(
SSHORT char_set_id,
SSHORT collation,
bool collate_only,
TEXT* string)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ g e t _ c h a r a c t e r _ s e t s
*
**************************************
*
* Retrieve character set and collation order and format it
*
**************************************/
#ifdef DEV_BUILD
bool found = false;
2001-05-23 15:26:42 +02:00
#endif
string[0] = 0;
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return;
}
2001-05-23 15:26:42 +02:00
//if (collation) {
if (collation || collate_only) {
FOR FIRST 1 COL IN RDB$COLLATIONS CROSS
CST IN RDB$CHARACTER_SETS WITH
COL.RDB$CHARACTER_SET_ID EQ CST.RDB$CHARACTER_SET_ID AND
COL.RDB$COLLATION_ID EQ collation AND
CST.RDB$CHARACTER_SET_ID EQ char_set_id
SORTED BY COL.RDB$COLLATION_NAME, CST.RDB$CHARACTER_SET_NAME
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
found = true;
2001-05-23 15:26:42 +02:00
#endif
fb_utils::exact_name(CST.RDB$CHARACTER_SET_NAME);
fb_utils::exact_name(COL.RDB$COLLATION_NAME);
fb_utils::exact_name(CST.RDB$DEFAULT_COLLATE_NAME);
/* Is specified collation the default collation for character set? */
if (strcmp (CST.RDB$DEFAULT_COLLATE_NAME, COL.RDB$COLLATION_NAME) == 0) {
if (!collate_only)
sprintf (string, " CHARACTER SET %s", CST.RDB$CHARACTER_SET_NAME);
}
else if (collate_only)
sprintf (string, " COLLATE %s", COL.RDB$COLLATION_NAME);
else
sprintf (string, " CHARACTER SET %s COLLATE %s",
CST.RDB$CHARACTER_SET_NAME, COL.RDB$COLLATION_NAME);
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return;
2001-05-23 15:26:42 +02:00
END_ERROR;
#ifdef DEV_BUILD
if (!found) {
sprintf(Print_buffer,
"ISQL_get_character_set: charset %d collation %d not found.\n",
char_set_id, collation);
STDERROUT(Print_buffer, 1);
}
#endif
}
else {
FOR FIRST 1 CST IN RDB$CHARACTER_SETS WITH
CST.RDB$CHARACTER_SET_ID EQ char_set_id
SORTED BY CST.RDB$CHARACTER_SET_NAME
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
found = true;
2001-05-23 15:26:42 +02:00
#endif
fb_utils::exact_name(CST.RDB$CHARACTER_SET_NAME);
sprintf (string, " CHARACTER SET %s", CST.RDB$CHARACTER_SET_NAME);
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return;
END_ERROR;
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
if (!found) {
sprintf(Print_buffer, "ISQL_get_character_set: charset %d not found.\n",
2001-05-23 15:26:42 +02:00
char_set_id);
STDERROUT(Print_buffer, 1);
}
#endif
}
}
void ISQL_get_default_source(const TEXT* rel_name,
TEXT* field_name,
ISC_QUAD* blob_id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ g e t _ d e f a u l t _ s o u r c e
*
**************************************
*
* Retrieve the default source of a field.
* Relation_fields takes precedence over fields if both
* are present
*
* For a domain, a NULL is passed for rel_name
**************************************/
fb_utils::exact_name(field_name);
2001-05-23 15:26:42 +02:00
*blob_id = isc_blob_null;
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return;
}
2001-05-23 15:26:42 +02:00
if (rel_name) {
// This is default for a column of a table
FOR FLD IN RDB$FIELDS CROSS
RFR IN RDB$RELATION_FIELDS WITH
RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
RFR.RDB$RELATION_NAME EQ rel_name AND
FLD.RDB$FIELD_NAME EQ field_name
if (!RFR.RDB$DEFAULT_SOURCE.NULL)
*blob_id = RFR.RDB$DEFAULT_SOURCE;
else if (!FLD.RDB$DEFAULT_SOURCE.NULL)
*blob_id = FLD.RDB$DEFAULT_SOURCE;
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return;
2001-05-23 15:26:42 +02:00
END_ERROR;
}
else { // default for a domain
2001-05-23 15:26:42 +02:00
FOR FLD IN RDB$FIELDS WITH
FLD.RDB$FIELD_NAME EQ field_name
if (!FLD.RDB$DEFAULT_SOURCE.NULL)
*blob_id = FLD.RDB$DEFAULT_SOURCE;
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return;
2001-05-23 15:26:42 +02:00
END_ERROR;
}
}
SLONG ISQL_get_index_segments(TEXT* segs,
const TEXT* indexname,
bool delimited_yes)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ g e t _ i n d e x _ s e g m e n t s
*
**************************************
*
* Functional description
* returns the list of columns in an index.
*
**************************************/
TEXT SQL_identifier[BUFFER_LENGTH128];
*segs = '\0';
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return 0;
}
2001-05-23 15:26:42 +02:00
// Query to get column names
SLONG n = 0;
2001-05-23 15:26:42 +02:00
FOR SEG IN RDB$INDEX_SEGMENTS WITH
SEG.RDB$INDEX_NAME EQ indexname
SORTED BY SEG.RDB$FIELD_POSITION
++n;
// Place a comma and a blank between each segment column name
fb_utils::exact_name(SEG.RDB$FIELD_NAME);
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) {
ISQL_copy_SQL_id (SEG.RDB$FIELD_NAME, SQL_identifier, DBL_QUOTE);
}
else {
strcpy (SQL_identifier, SEG.RDB$FIELD_NAME);
}
if (n == 1)
sprintf (segs + strlen(segs), "%s", SQL_identifier);
else
sprintf (segs + strlen(segs), ", %s", SQL_identifier);
2001-05-23 15:26:42 +02:00
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
ROLLBACK;
return 0;
2001-05-23 15:26:42 +02:00
END_ERROR;
return (n);
}
bool ISQL_get_base_column_null_flag(const TEXT* view_name,
const SSHORT view_context,
const TEXT* base_field)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ g e t _ b a s e _ c o l u m n _ n u l l _ f l a g
*
**************************************
*
* Determine if a field on which view column is based
* is nullable. We are passed the view_name
* view_context and the base_field of the view column.
*
**************************************/
BASED_ON RDB$RELATION_FIELDS.RDB$RELATION_NAME save_view_name,
save_base_field;
strcpy(save_view_name, view_name);
strcpy(save_base_field, base_field);
SSHORT save_view_context = view_context;
2001-05-23 15:26:42 +02:00
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return false;
}
2001-05-23 15:26:42 +02:00
/*
Using view_name and view_context get the relation name from
RDB$VIEW_RELATIONS which contains the base_field for this view column.
Get row corresponding to this base field and relation from
2001-05-23 15:26:42 +02:00
rdb$field_relations. This will contain info on field's nullability unless
it is a view column itself, in which case repeat this procedure till
we get to a "real" column.
2001-05-23 15:26:42 +02:00
*/
bool null_flag = true;
bool done = false;
bool error = false;
2001-05-23 15:26:42 +02:00
while (!done && !error) {
fb_utils::exact_name(save_view_name);
fb_utils::exact_name(save_base_field);
FOR FIRST 1
VR IN RDB$VIEW_RELATIONS
CROSS NEWRFR IN RDB$RELATION_FIELDS
WITH
VR.RDB$VIEW_NAME EQ save_view_name AND
VR.RDB$VIEW_CONTEXT EQ save_view_context AND
NEWRFR.RDB$RELATION_NAME = VR.RDB$RELATION_NAME AND
NEWRFR.RDB$FIELD_NAME = save_base_field
if (NEWRFR.RDB$BASE_FIELD.NULL) {
if (NEWRFR.RDB$NULL_FLAG == 1)
null_flag = false;
done = true;
}
else {
strcpy (save_view_name, NEWRFR.RDB$RELATION_NAME);
save_view_context = NEWRFR.RDB$VIEW_CONTEXT;
strcpy (save_base_field, NEWRFR.RDB$BASE_FIELD);
}
END_FOR
ON_ERROR
error = true;
2001-05-23 15:26:42 +02:00
END_ERROR;
}
return null_flag;
}
bool ISQL_get_null_flag(const TEXT* rel_name,
TEXT* field_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ g e t _ n u l l _ f l a g
*
**************************************
*
* Determine if a field has the null flag set.
* Look for either rdb$relation_fields or rdb$fields to be
2001-05-23 15:26:42 +02:00
* Set to 1 (NOT NULL), then this field cannot be null
* We are passed the relation name and the relation_field name
* For domains, the relation name is null.
**************************************/
fb_utils::exact_name(field_name);
2001-05-23 15:26:42 +02:00
bool null_flag = true;
2001-05-23 15:26:42 +02:00
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return false;
}
2001-05-23 15:26:42 +02:00
if (rel_name) {
FOR FLD IN RDB$FIELDS CROSS
RFR IN RDB$RELATION_FIELDS WITH
RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
RFR.RDB$RELATION_NAME EQ rel_name AND
RFR.RDB$FIELD_NAME EQ field_name
if (FLD.RDB$NULL_FLAG == 1)
null_flag = false;
else {
2004-03-07 08:58:55 +01:00
// If RDB$BASE_FIELD is not null then it is a view column
if (RFR.RDB$BASE_FIELD.NULL) {
/* Simple column. Did user define it not null? */
if (RFR.RDB$NULL_FLAG == 1)
null_flag = false;
}
else
null_flag = ISQL_get_base_column_null_flag (rel_name,
RFR.RDB$VIEW_CONTEXT, RFR.RDB$BASE_FIELD);
}
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return null_flag;
2001-05-23 15:26:42 +02:00
END_ERROR;
}
else {
// Domains have only field entries to worry about
FOR FLD IN RDB$FIELDS WITH
FLD.RDB$FIELD_NAME EQ field_name
if (FLD.RDB$NULL_FLAG == 1)
null_flag = false;
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return null_flag;
2001-05-23 15:26:42 +02:00
END_ERROR;
}
return null_flag;
}
void ISQL_disconnect_database(bool nQuietMode)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ d i s c o n n e c t _ d a t a b a s e
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Disconnect from the current database. First commit work and then
2001-05-23 15:26:42 +02:00
* call isc_detach_database to detach from the database and the zero
* out the DB handle.
*
**************************************/
// Ignore error msgs during disconnect
2001-05-23 15:26:42 +02:00
Quiet = nQuietMode;
// If we were in a database, commit before proceding
2001-05-23 15:26:42 +02:00
if (DB && (M__trans || D__trans))
end_trans();
/*
* Commit transaction that was started on behalf of the request
* Don't worry about the error if one occurs it will be network
2001-05-23 15:26:42 +02:00
* related and caught later.
*/
if (DB && gds_trans)
isc_rollback_transaction(isc_status, &gds_trans);
2001-05-23 15:26:42 +02:00
/* 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);
2001-05-23 15:26:42 +02:00
// Detach from old database
if (DB) {
isc_detach_database(isc_status, &DB);
}
2001-05-23 15:26:42 +02:00
// Enable error msgs
Quiet = false;
2001-05-23 15:26:42 +02:00
// Zero database handle and transaction handles
2004-05-03 01:06:37 +02:00
global_Stmt = 0;
DB = 0;
isqlGlob.global_Db_name[0] = '\0';
2004-05-03 01:06:37 +02:00
D__trans = 0;
M__trans = 0;
gds_trans = 0;
// CVC: If we aren't connected to a db anymore, then the db's dialect is reset.
// This should fix SF Bug #910430.
isqlGlob.db_SQL_dialect = 0;
// BRS this is also needed to fix #910430.
dialect_spoken = 0;
2001-05-23 15:26:42 +02:00
return;
}
#ifdef NOT_USED_OR_REPLACED
bool ISQL_is_domain(const TEXT* field_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ i s _ d o m a i n
*
**************************************
*
* Determine if a field in rdb$fields is a domain,
*
**************************************/
bool is_domain = false;
2001-05-23 15:26:42 +02:00
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return false;
}
2001-05-23 15:26:42 +02:00
FOR FLD IN RDB$FIELDS WITH
FLD.RDB$FIELD_NAME EQ field_name
if (!(strncmp (FLD.RDB$FIELD_NAME, "RDB$", 4) == 0 &&
isdigit (FLD.RDB$FIELD_NAME[4]) &&
FLD.RDB$SYSTEM_FLAG != 1))
{
is_domain = true;
}
2001-05-23 15:26:42 +02:00
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
return is_domain;
2001-05-23 15:26:42 +02:00
END_ERROR;
return is_domain;
}
#endif
2001-05-23 15:26:42 +02:00
void ISQL_make_upper(TEXT* str)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ m a k e _ u p p e r
*
**************************************
*
* Force the name of a metadata object to
* uppercase.
*
**************************************/
if (!str)
return;
for (UCHAR* p = reinterpret_cast<UCHAR*>(str); *p; p++)
2001-05-23 15:26:42 +02:00
*p = UPPER7(*p);
}
void ISQL_msg_get(USHORT number,
TEXT* msg,
const TEXT* arg1,
const TEXT* arg2,
const TEXT* arg3,
const TEXT* arg4,
const TEXT* arg5)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ m s g _ g e t
*
**************************************
*
* Functional description
* Retrieve a message from the error file
*
**************************************/
gds__msg_format(NULL, ISQL_MSG_FAC, number, MSG_LENGTH, msg,
2001-05-23 15:26:42 +02:00
arg1, arg2, arg3, arg4, arg5);
}
2004-04-29 00:36:29 +02:00
void ISQL_print_validation(FILE* fp,
ISC_QUAD* blobid,
2003-09-09 13:03:37 +02:00
bool isComputedField,
2004-05-03 01:06:37 +02:00
FB_API_HANDLE trans)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ p r i n t _ v a l i d a t i o n
*
**************************************
*
* Functional description
* This does some minor syntax adjustmet for extracting
2001-05-23 15:26:42 +02:00
* validation blobs and computed fields.
* if it does not start with the word CHECK
* if this is a computed field blob, look for () or insert them.
2001-05-23 15:26:42 +02:00
* if flag == 0, this is a validation clause,
* if flag == 1, this is a computed field
*
**************************************/
// Don't bother with null blobs
2001-05-23 15:26:42 +02:00
if (blobid->gds_quad_high == 0)
2001-05-23 15:26:42 +02:00
return;
TEXT* buffer = (TEXT*) ISQL_ALLOC(BUFFER_LENGTH512);
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
FB_API_HANDLE blob = 0;
if (isc_open_blob(isc_status, &DB, &trans, &blob, blobid)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
if (buffer)
ISQL_FREE(buffer);
return;
}
bool issql = false;
bool firsttime = true;
USHORT length;
while (!isc_get_segment(isc_status, &blob, &length,
2001-05-23 15:26:42 +02:00
(USHORT) (BUFFER_LENGTH512 - 1),
buffer) || isc_status[1] == isc_segment)
{
2001-05-23 15:26:42 +02:00
buffer[length] = 0;
const TEXT* p = buffer;
2003-09-09 13:03:37 +02:00
if (isComputedField) {
// computed field SQL syntax correction
2001-05-23 15:26:42 +02:00
while (fb_isspace(*p))
2001-05-23 15:26:42 +02:00
p++;
if (*p == '(')
issql = true;
2001-05-23 15:26:42 +02:00
}
else {
// Validation SQL syntax correction
2001-05-23 15:26:42 +02:00
while (fb_isspace(*p))
2001-05-23 15:26:42 +02:00
p++;
if (!strncmp(p, "check", 5) || !strncmp(p, "CHECK", 5))
issql = true;
2001-05-23 15:26:42 +02:00
}
if (firsttime) {
firsttime = false;
2001-05-23 15:26:42 +02:00
if (!issql) {
2003-09-09 13:03:37 +02:00
sprintf(Print_buffer, "%s ", (isComputedField ? "/* " : "("));
2001-05-23 15:26:42 +02:00
ISQL_printf(fp, Print_buffer);
}
}
ISQL_printf(fp, buffer);
}
if (!issql) {
2003-09-09 13:03:37 +02:00
sprintf(Print_buffer, "%s", (isComputedField ? " */" : ")"));
2001-05-23 15:26:42 +02:00
ISQL_printf(fp, Print_buffer);
}
isc_close_blob(isc_status, &blob);
2001-05-23 15:26:42 +02:00
if (isc_status[1])
ISQL_errmsg(isc_status);
if (buffer)
ISQL_FREE(buffer);
}
2004-04-29 00:36:29 +02:00
void ISQL_printf(FILE* fp,
const char* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ p r i n t f
*
**************************************
*
* Centralized printing facility
*
**************************************/
2004-04-29 00:36:29 +02:00
fprintf(fp, "%s", buffer);
fflush(fp); // John's fix.
2001-05-23 15:26:42 +02:00
}
static processing_state add_row(const TEXT* tabname)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* a d d _ r o w
*
**************************************
*
* Functional description
* Allows interactive insert of row, prompting for every column
*
* The technique is to describe a select query of select * from the table.
* Then generate an insert with ? in every position, using the same sqlda.
* It will allow edits of blobs, skip arrays and computed fields
*
* Parameters:
* tabname -- Name of table to insert into
*
**************************************/
SCHAR cmd[5];
// Data types
SSHORT* smallint;
SSHORT dscale;
SLONG* integer;
SINT64* pi64;
SINT64 n;
float* fvalue;
double* dvalue;
2003-09-17 12:49:29 +02:00
tm times;
ISC_QUAD* blobid;
2001-05-23 15:26:42 +02:00
TEXT msg[MSG_LENGTH];
SLONG res;
if (!*tabname)
return (ERR);
if (!Interactive)
return (ERR);
// Initialize the time structure
memset(&times, 0, sizeof(times));
2001-05-23 15:26:42 +02:00
// There may have been a commit transaction before we got here
2001-05-23 15:26:42 +02:00
if (!M__trans)
if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return FAIL;
}
2001-05-23 15:26:42 +02:00
// Start default transaction for prepare
2001-05-23 15:26:42 +02:00
if (!D__trans)
if (isc_start_transaction(isc_status, &D__trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return FAIL;
}
2001-05-23 15:26:42 +02:00
// Query to obtain relation information
SCHAR string[BUFFER_LENGTH120];
2001-05-23 15:26:42 +02:00
sprintf(string, "SELECT * FROM %s", tabname);
XSQLDA* sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(20)));
2001-05-23 15:26:42 +02:00
sqlda->version = SQLDA_VERSION1;
sqlda->sqln = 20;
if (isc_dsql_prepare(isc_status, &D__trans, &global_Stmt, 0, string,
isqlGlob.SQL_dialect, sqlda))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
if (sqlda)
ISQL_FREE(sqlda);
2001-05-23 15:26:42 +02:00
return (SKIP);
}
const SSHORT n_cols = sqlda->sqld;
2001-05-23 15:26:42 +02:00
// Reallocate to the proper size if necessary
2001-05-23 15:26:42 +02:00
if (sqlda->sqld > sqlda->sqln) {
ISQL_FREE(sqlda);
sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(n_cols)));
sqlda->version = SQLDA_VERSION1;
2001-05-23 15:26:42 +02:00
sqlda->sqld = sqlda->sqln = n_cols;
// We must re-describe the sqlda, no need to re-prepare
2001-05-23 15:26:42 +02:00
if (isc_dsql_describe(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
if (sqlda)
ISQL_FREE(sqlda);
return (FAIL);
}
2001-05-23 15:26:42 +02:00
}
// Array for storing select/insert column mapping , colcheck sets it up
SSHORT* colnumber = (SSHORT*) ISQL_ALLOC((SLONG) (n_cols * sizeof(SSHORT)));
2001-05-23 15:26:42 +02:00
col_check(tabname, colnumber);
// Create the new INSERT statement from the sqlda info
2001-05-23 15:26:42 +02:00
SCHAR* insertstring = (SCHAR*) ISQL_ALLOC((SLONG) (40 + 36 * n_cols));
2001-05-23 15:26:42 +02:00
// There is a question marker for each column known updatable column
2001-05-23 15:26:42 +02:00
sprintf(insertstring, "INSERT INTO %s (", tabname);
int i;
SSHORT i_cols = 0;
i = 0;
{ // scope
for (const XSQLVAR* var = sqlda->sqlvar; i < n_cols; var++, i++) {
// Skip columns that are computed
2001-05-23 15:26:42 +02:00
if (colnumber[i] != -1) {
sprintf(insertstring + strlen(insertstring), "%s,", var->sqlname);
i_cols++;
}
}
} // scope for MSVC6
// Set i_cols to the number of insert columns
2001-05-23 15:26:42 +02:00
insertstring[strlen(insertstring) - 1] = ')';
strcat(insertstring, " VALUES (");
for (i = 0; i < n_cols; i++) {
if (colnumber[i] != -1)
strcat(insertstring, " ?,");
}
// Patch the last character in the string
2001-05-23 15:26:42 +02:00
insertstring[strlen(insertstring) - 1] = ')';
// Allocate INSERT sqlda and null indicators
2001-05-23 15:26:42 +02:00
XSQLDA* isqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(i_cols)));
2001-05-23 15:26:42 +02:00
/* ISQL_ALLOC doesn't initialize the structure, so init everything
* to avoid lots of problems.
*/
memset(isqlda, 0, XSQLDA_LENGTH(i_cols));
2001-05-23 15:26:42 +02:00
isqlda->version = SQLDA_VERSION1;
isqlda->sqln = isqlda->sqld = i_cols;
SSHORT* nullind = (SSHORT*) ISQL_ALLOC((SLONG) (i_cols * sizeof(SSHORT)));
2001-05-23 15:26:42 +02:00
/*
** For each column, get the type, and length then prompt for a value
2004-04-29 00:36:29 +02:00
** and scanf the resulting string into a buffer of the right type.
2001-05-23 15:26:42 +02:00
*/
SCHAR infobuf[BUFFER_LENGTH180];
gds__msg_format(NULL, ISQL_MSG_FAC, ADD_PROMPT, sizeof(infobuf),
infobuf, NULL, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(infobuf, 1);
SCHAR bfile[120];
2001-05-23 15:26:42 +02:00
SCHAR name[WORDLENGTH];
bool done = false;
2001-05-23 15:26:42 +02:00
while (!done) {
SSHORT* nullp = nullind;
i = 0;
for (const XSQLVAR* var = sqlda->sqlvar; i < n_cols && !done; var++, i++) {
2001-05-23 15:26:42 +02:00
if (colnumber[i] == -1)
continue;
// SQLDA for INSERT statement might have fewer columns
const SSHORT j = colnumber[i];
XSQLVAR* ivar = &isqlda->sqlvar[j];
2001-05-23 15:26:42 +02:00
*ivar = *var;
//const SSHORT length = var->sqllen; UNUSED
2001-05-23 15:26:42 +02:00
*nullp = 0;
ivar->sqlind = nullp++;
ivar->sqldata = NULL;
strcpy(name, var->sqlname);
name[var->sqlname_length] = '\0';
const SSHORT type = var->sqltype & ~1;
const SSHORT nullable = var->sqltype & 1;
2001-05-23 15:26:42 +02:00
// Prompt with the name and read the line
2001-05-23 15:26:42 +02:00
if (type == SQL_BLOB) {
ISQL_msg_get(BLOB_PROMPT, msg, name, NULL, NULL, NULL, NULL);
/* Blob: %s, type 'edit' or filename to load> */
2001-05-23 15:26:42 +02:00
}
else if (type == SQL_TIMESTAMP || type == SQL_TYPE_DATE) {
ISQL_msg_get(DATE_PROMPT, msg, name, NULL, NULL, NULL, NULL);
/* Enter %s as M/D/Y> */
2001-05-23 15:26:42 +02:00
}
else {
ISQL_msg_get(NAME_PROMPT, msg, name, NULL, NULL, NULL, NULL);
/* Enter %s> */
2001-05-23 15:26:42 +02:00
}
// On blank line or EOF, break the loop without doing an insert
2001-05-23 15:26:42 +02:00
readNextInputLine(msg);
// ISQL_prompt(msg);
2004-04-29 00:36:29 +02:00
// if (!fgets(buffer, BUFFER_LENGTH512, stdin)
// || !strlen(buffer)) {
if (lastInputLine == NULL || strlen(lastInputLine) == 0) {
done = true;
break;
}
2001-05-23 15:26:42 +02:00
#if defined(DEV_BUILD)
{ // scope
const size_t length_aux = strlen(lastInputLine);
fb_assert(length_aux <= MAX_USHORT - 2);
} // scope
#endif
const USHORT length = (USHORT) strlen(lastInputLine);
2001-05-23 15:26:42 +02:00
STDERROUT("", 1);
// Convert first 4 chars to upper case for comparison
strncpy(cmd, lastInputLine, 4);
2001-05-23 15:26:42 +02:00
cmd[4] = '\0';
ISQL_make_upper(cmd);
2001-05-23 15:26:42 +02:00
// If the user writes NULL, put a null in the column
2001-05-23 15:26:42 +02:00
if (!strcmp(cmd, "NULL"))
*ivar->sqlind = -1;
else
switch (type) {
case SQL_BLOB:
blobid =
(ISC_QUAD*) ISQL_ALLOC((SLONG) sizeof(ISC_QUAD));
2001-05-23 15:26:42 +02:00
if (!strcmp(cmd, "EDIT"))
// If edit, we edit a temp file
2001-05-23 15:26:42 +02:00
{
tmpnam(bfile);
gds__edit(bfile, 0);
res = BLOB_text_load(blobid, DB, M__trans, bfile);
unlink(bfile);
}
else
// Otherwise just load the named file
2001-05-23 15:26:42 +02:00
/* We can't be sure if it's a TEXT or BINARY file
* being loaded. As we aren't sure, we'll
* do binary operation - this is NEW data in
* the database, not updating existing info
*/
{
strcpy(bfile, lastInputLine);
2001-05-23 15:26:42 +02:00
res = BLOB_load(blobid, DB, M__trans, bfile);
}
if (!res) {
STDERROUT("Unable to load file", 1);
done = true;
2001-05-23 15:26:42 +02:00
}
ivar->sqldata = (SCHAR*) blobid;
2001-05-23 15:26:42 +02:00
break;
case SQL_FLOAT:
fvalue = (float*) ISQL_ALLOC(sizeof(float));
if (sscanf(lastInputLine, "%g", fvalue) != 1) {
2001-05-23 15:26:42 +02:00
STDERROUT("Input parsing problem", 1);
done = true;
2001-05-23 15:26:42 +02:00
}
ivar->sqldata = (SCHAR*) fvalue;
2001-05-23 15:26:42 +02:00
break;
case SQL_DOUBLE:
dvalue = (double*) ISQL_ALLOC(sizeof(double));
if (sscanf(lastInputLine, "%lg", dvalue) != 1) {
2001-05-23 15:26:42 +02:00
STDERROUT("Input parsing problem", 1);
done = true;
2001-05-23 15:26:42 +02:00
}
ivar->sqldata = (SCHAR*) dvalue;
2001-05-23 15:26:42 +02:00
break;
case SQL_TYPE_TIME:
if (3 != sscanf(lastInputLine, "%d:%d:%d", &times.tm_hour,
&times.tm_min, &times.tm_sec))
{
ISQL_msg_get(TIME_ERR, msg, lastInputLine, NULL, NULL, NULL,
2001-05-23 15:26:42 +02:00
NULL);
2004-03-07 08:58:55 +01:00
STDERROUT(msg, 1); // Bad date %s\n
done = true;
2001-05-23 15:26:42 +02:00
}
else {
ULONG date[2];
char* stringvalue =
(SCHAR*) ISQL_ALLOC((SLONG) (ivar->sqllen + 1));
isc_encode_date(&times, (ISC_QUAD*) date);
2001-05-23 15:26:42 +02:00
ivar->sqldata = stringvalue;
*(ULONG*) stringvalue = date[1];
}
2001-05-23 15:26:42 +02:00
break;
case SQL_TIMESTAMP:
case SQL_TYPE_DATE:
if (3 != sscanf(lastInputLine, "%d/%d/%d", &times.tm_mon,
&times.tm_mday, &times.tm_year))
{
ISQL_msg_get(DATE_ERR, msg, lastInputLine, NULL, NULL, NULL,
2001-05-23 15:26:42 +02:00
NULL);
2004-03-07 08:58:55 +01:00
STDERROUT(msg, 1); // Bad date %s\n
done = true;
2001-05-23 15:26:42 +02:00
}
else {
ULONG date[2];
times.tm_mon--;
times.tm_year -= 1900; // tm_year is 1900-based.
char* stringvalue =
(SCHAR*) ISQL_ALLOC((SLONG) (ivar->sqllen + 1));
isc_encode_date(&times, (ISC_QUAD*) date);
2001-05-23 15:26:42 +02:00
ivar->sqldata = stringvalue;
((ULONG*) stringvalue)[0] = date[0];
2001-05-23 15:26:42 +02:00
if (type == SQL_TIMESTAMP)
((ULONG*) stringvalue)[1] = date[1];
2001-05-23 15:26:42 +02:00
}
break;
case SQL_TEXT:
// Force all text to varying
2001-05-23 15:26:42 +02:00
ivar->sqltype = SQL_VARYING + nullable;
case SQL_VARYING:
{
vary* avary =
(vary*)
2001-05-23 15:26:42 +02:00
ISQL_ALLOC((SLONG) (length + sizeof(USHORT)));
avary->vary_length = length;
strncpy(avary->vary_string, lastInputLine, length);
ivar->sqldata = (SCHAR*) avary;
2001-05-23 15:26:42 +02:00
ivar->sqllen = length + sizeof(USHORT);
break;
}
2001-05-23 15:26:42 +02:00
case SQL_SHORT:
case SQL_LONG:
if (type == SQL_SHORT) {
smallint = (SSHORT*) ISQL_ALLOC(sizeof(SSHORT));
ivar->sqldata = (SCHAR*) smallint;
2001-05-23 15:26:42 +02:00
}
else if (type == SQL_LONG) {
integer = (SLONG*) ISQL_ALLOC(sizeof(SLONG));
ivar->sqldata = (SCHAR*) integer;
2001-05-23 15:26:42 +02:00
}
// Fall through from SQL_SHORT and SQL_LONG ...
2001-05-23 15:26:42 +02:00
case SQL_INT64:
if (type == SQL_INT64) {
pi64 = (SINT64*) ISQL_ALLOC(sizeof(SINT64));
ivar->sqldata = (SCHAR*) pi64;
2001-05-23 15:26:42 +02:00
}
if ((dscale = var->sqlscale) < 0) {
SSHORT lscale = 0;
if (get_numeric((UCHAR*) lastInputLine, length, &lscale, &n) != true) {
2001-05-23 15:26:42 +02:00
STDERROUT("Input parsing problem", 1);
done = true;
2001-05-23 15:26:42 +02:00
}
else {
dscale = var->sqlscale - lscale;
if (dscale > 0) {
2003-04-08 12:38:59 +02:00
TEXT err_buf[256];
sprintf(err_buf,
2001-05-23 15:26:42 +02:00
"input error: input scale %d exceeds the field's scale %d",
-lscale, -var->sqlscale);
STDERROUT(err_buf, 1);
done = true;
2001-05-23 15:26:42 +02:00
}
while (dscale++ < 0)
n *= 10;
}
}
// sscanf assumes an 64-bit integer target
else if (sscanf(lastInputLine, "%" QUADFORMAT "d", &n) != 1) {
2001-05-23 15:26:42 +02:00
STDERROUT("Input parsing problem", 1);
done = true;
2001-05-23 15:26:42 +02:00
}
if (type == SQL_INT64)
*pi64 = n;
else if (type == SQL_SHORT)
*smallint = n;
else if (type == SQL_LONG)
*integer = n;
break;
default:
done = true;
2001-05-23 15:26:42 +02:00
STDERROUT("Unexpected SQLTYPE in add_row()", 1);
break;
}
}
if (!done) {
// having completed all columns, try the insert statement with the sqlda
2001-05-23 15:26:42 +02:00
if (isc_dsql_exec_immed2(isc_status, &DB, &M__trans, 0,
insertstring, isqlGlob.SQL_dialect, isqlda, NULL))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
break;
}
// Release each of the variables for next time
2001-05-23 15:26:42 +02:00
XSQLVAR* ivar = isqlda->sqlvar;
for (i = 0; i < i_cols; i++, ivar++)
2001-05-23 15:26:42 +02:00
if (ivar->sqldata)
ISQL_FREE(ivar->sqldata);
}
}
ISQL_FREE(insertstring);
XSQLVAR* ivar = isqlda->sqlvar;
for (i = 0; i < i_cols; i++, ivar++)
2001-05-23 15:26:42 +02:00
if (ivar->sqldata)
ISQL_FREE(ivar->sqldata);
if (sqlda)
ISQL_FREE(sqlda);
if (isqlda)
ISQL_FREE(isqlda);
if (colnumber)
ISQL_FREE(colnumber);
if (nullind)
ISQL_FREE(nullind);
return (SKIP);
}
2002-06-29 15:39:11 +02:00
2001-05-23 15:26:42 +02:00
static processing_state blobedit(const TEXT* action,
const TEXT* const* cmd)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* b l o b e d i t
*
**************************************
*
* Functional description
* Edit the text blob indicated by blobid
*
* Parameters: cmd -- Array of words interpreted as file name
*
**************************************/
if (!ISQL_dbcheck())
return FAIL;
if (*cmd[1] == 0)
return ERR;
const TEXT* p = cmd[1];
2001-05-23 15:26:42 +02:00
// Find the high and low values of the blob id
ISC_QUAD blobid;
2003-04-08 12:38:59 +02:00
sscanf(p, "%"xLONGFORMAT":%"xLONGFORMAT, &blobid.gds_quad_high, &blobid.gds_quad_low);
2001-05-23 15:26:42 +02:00
/* If it isn't an explicit blobedit, then do a dump. Since this is a
2001-05-23 15:26:42 +02:00
user operation, put it on the M__trans handle */
if (!strcmp(action, "BLOBVIEW"))
BLOB_edit(&blobid, DB, M__trans, "blob");
2001-05-23 15:26:42 +02:00
else if ((!strcmp(action, "BLOBDUMP")) && (*cmd[2])) {
// If this is a blobdump, make sure there is a file name
2001-05-23 15:26:42 +02:00
/* We can't be sure if the BLOB is TEXT or BINARY data,
* as we're not sure, we'll dump it in BINARY mode
*/
BLOB_dump(&blobid, DB, M__trans, cmd[2]);
2001-05-23 15:26:42 +02:00
}
else
return ERR;
return (SKIP);
}
static void col_check(const TEXT* tablename,
SSHORT* colnumber)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o l _ c h e c k
*
**************************************
*
* Check for peculiarities not currently revealed by the SQLDA
* colnumber array records the mapping of select columns to insert
* columns which do not have an equivalent for array or computed cols.
**************************************/
// Query to get array info and computed source not available in the sqlda
2001-05-23 15:26:42 +02:00
//ISQL_make_upper(tabname); // CVC: This won't catch mixed case file names!
TEXT tabname[WORDLENGTH * 2];
const bool delimited_yes = tablename[0] == DBL_QUOTE;
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) {
ISQL_copy_SQL_id(tablename, tabname, DBL_QUOTE);
}
else {
strcpy(tabname, tablename);
}
2001-05-23 15:26:42 +02:00
// Transaction for all frontend commands
if (DB && !gds_trans) {
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return;
}
}
SSHORT i = 0, j = 0;
FOR F IN RDB$FIELDS CROSS
R IN RDB$RELATION_FIELDS WITH
F.RDB$FIELD_NAME = R.RDB$FIELD_SOURCE AND
R.RDB$RELATION_NAME EQ tabname
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;
else
colnumber[i] = j++;
++i;
END_FOR
ON_ERROR
ISQL_errmsg(isc_status);
2001-05-23 15:26:42 +02:00
END_ERROR;
}
static void copy_str(TEXT** output_str,
const TEXT** input_str,
bool* done,
const TEXT* const str_begin,
const TEXT* const str_end,
literal_string_type str_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o p y _ s t r
*
**************************************
*
* Functional description
*
* Copy and/or modify the data in the input buffer and stores it
* into output buffer.
*
* For SINGLE_QUOTED_STRING, copy everything from the begining of the
* the input buffer to the end of the string constant
*
* For DOUBLE_QUOTED_STRING, copy everything from the begining of the
* the input buffer to the begining of the string constant
*
* a. replace double quote string delimted character with single
2001-05-23 15:26:42 +02:00
* quote
* b. if '""' is found with in the string constant, removes one
2001-05-23 15:26:42 +02:00
* extra double quote
* c. if a single quote is found with in the string constant,
2001-05-23 15:26:42 +02:00
* adds one more single quote
* d. replace ending double quote string delimted charcter with
2001-05-23 15:26:42 +02:00
* single quote
*
* For NO_MORE_STRING, copy everything from the begining of the
* the input buffer to the end of input buffer
*
* Input Arguments:
*
* 1. address of the output buffer
* 2. address of the input buffer
* 3. address of the end of processing flag
*
* Output Arguments:
*
* 1. pointer to begining of the string constant
* 2. pointer to ending of the string constant
* 3. string type
*
**************************************/
SSHORT slen;
const TEXT* b1;
2001-05-23 15:26:42 +02:00
TEXT* temp_str = *output_str;
const TEXT* temp_local_stmt_str = *input_str;
2001-05-23 15:26:42 +02:00
switch (str_flag) {
case SINGLE_QUOTED_STRING:
slen = str_end - temp_local_stmt_str;
strncpy(temp_str, temp_local_stmt_str, slen);
*output_str = temp_str + slen;
*input_str = str_end;
break;
case DOUBLE_QUOTED_STRING:
slen = str_begin - temp_local_stmt_str;
strncpy(temp_str, temp_local_stmt_str, slen);
temp_str += slen;
*temp_str = SINGLE_QUOTE;
temp_str++;
b1 = str_begin + 1;
2002-06-29 15:39:11 +02:00
while (b1 < str_end) {
2001-05-23 15:26:42 +02:00
*temp_str = *b1;
temp_str++;
switch (*b1) {
case SINGLE_QUOTE:
*temp_str = SINGLE_QUOTE;
temp_str++;
break;
case DBL_QUOTE:
b1++;
if (*b1 != DBL_QUOTE) {
temp_str--;
*temp_str = SINGLE_QUOTE;
temp_str++;
b1--;
}
break;
default:
break;
}
b1++;
}
*output_str = temp_str;
*input_str = b1;
break;
case NO_MORE_STRING:
slen = strlen(temp_local_stmt_str);
strncpy(temp_str, temp_local_stmt_str, slen);
temp_str += slen;
*temp_str = '\0';
*done = true;
2001-05-23 15:26:42 +02:00
*output_str = temp_str;
*input_str = temp_local_stmt_str + slen;
break;
default:
break;
}
}
static processing_state copy_table(const TEXT* source_tbl,
const TEXT* destination,
const TEXT* otherdb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o p y _ t a b l e
*
**************************************
*
* Functional description
* Create a new table based on an existing one.
*
2001-05-23 15:26:42 +02:00
* Parameters: source -- name of source table
* destination == name of newly created table
*
2001-05-23 15:26:42 +02:00
**************************************/
TEXT errbuf[MSG_LENGTH];
/* Call list_table with a temporary file, then hand that file to a
new version of isql */
FILE* const holdout = isqlGlob.Out;
2001-05-23 15:26:42 +02:00
// If there is an alternate database, extract the domains
bool domain_flag = false;
2001-05-23 15:26:42 +02:00
if (*otherdb)
domain_flag = true;
2001-05-23 15:26:42 +02:00
TEXT ftmp[128];
isqlGlob.Out = (FILE*) gds__temp_file(TRUE, SCRATCH, ftmp);
if (isqlGlob.Out == (FILE*) - 1) {
// If we can't open a temp file then bail
2001-05-23 15:26:42 +02:00
gds__msg_format(NULL, ISQL_MSG_FAC, FILE_OPEN_ERR, sizeof(errbuf),
errbuf, ftmp, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
Exit_value = FINI_ERROR;
return END;
}
//ISQL_make_upper(source); // CVC: This won't catch mixed case table names!
TEXT source[WORDLENGTH * 2];
const bool delimited_yes = source_tbl[0] == DBL_QUOTE;
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) {
ISQL_copy_SQL_id(source_tbl, source, DBL_QUOTE);
}
else {
strcpy(source, source_tbl);
}
2001-05-23 15:26:42 +02:00
if (EXTRACT_list_table(source, destination, domain_flag, -1)) {
gds__msg_format(NULL, ISQL_MSG_FAC, NOT_FOUND, sizeof(errbuf),
errbuf, source, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
fclose(isqlGlob.Out);
2001-05-23 15:26:42 +02:00
}
else {
fclose(isqlGlob.Out);
2001-05-23 15:26:42 +02:00
// easy to make a copy in another database
const TEXT* altdb = isqlGlob.global_Db_name;
2001-05-23 15:26:42 +02:00
if (*otherdb)
altdb = otherdb;
TEXT cmd[512];
2001-05-23 15:26:42 +02:00
sprintf(cmd, "isql -q %s -i %s", altdb, ftmp);
if (system(cmd)) {
gds__msg_format(NULL, ISQL_MSG_FAC, COPY_ERR, sizeof(errbuf),
errbuf, destination, altdb, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
}
}
unlink(ftmp);
isqlGlob.Out = holdout;
2001-05-23 15:26:42 +02:00
return (SKIP);
}
static processing_state create_db(const TEXT* statement,
const TEXT* d_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c r e a t e _ d b
*
**************************************
*
* Functional description
* Intercept create database commands to
* adjust the DB and transaction handles
*
2001-05-23 15:26:42 +02:00
* Parameters: statement == the entire statement for processing.
*
* Note: SQL ROLE settings are ignored - the newly created database
* will not have any roles defined in it.
*
2001-05-23 15:26:42 +02:00
**************************************/
processing_state ret = SKIP;
2001-05-23 15:26:42 +02:00
// Disconnect from the database and cleanup
ISQL_disconnect_database(false);
2001-05-23 15:26:42 +02:00
SLONG arglength = strlen(statement) + strlen(isqlGlob.User) + strlen(Password) + 24;
2001-05-23 15:26:42 +02:00
if (*ISQL_charset && strcmp(ISQL_charset, DEFCHARSET))
arglength += strlen(ISQL_charset) + strlen(" SET NAMES \'\' ");
TEXT* local_statement = (TEXT*) ISQL_ALLOC(arglength + 1);
2001-05-23 15:26:42 +02:00
if (!local_statement)
return (FAIL);
TEXT* usr = (TEXT*) ISQL_ALLOC(USER_LENGTH);
2001-05-23 15:26:42 +02:00
if (!usr) {
ISQL_FREE(local_statement);
return (FAIL);
}
TEXT* psw = (TEXT*) ISQL_ALLOC(PASSWORD_LENGTH);
2001-05-23 15:26:42 +02:00
if (!psw) {
ISQL_FREE(local_statement);
ISQL_FREE(usr);
return (FAIL);
}
strcpy(local_statement, statement);
TEXT quote = DBL_QUOTE;
const TEXT* p = NULL;
2001-05-23 15:26:42 +02:00
// If there is a user parameter, we will set it into the create stmt.
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
2001-05-23 15:26:42 +02:00
if (!fAnsiCP || global_usr || global_psw ||
#else
if (global_usr || global_psw ||
#endif
(*ISQL_charset && strcmp(ISQL_charset, DEFCHARSET)))
{
strip_quotes(isqlGlob.User, usr);
2001-05-23 15:26:42 +02:00
strip_quotes(Password, psw);
// Look for the quotes on the database name and find the close quotes.
// Use '"' first, if not successful try '''.
// CVC: Again, this is wrong with embedded quotes.
const TEXT* q = strchr(statement, quote);
2001-05-23 15:26:42 +02:00
if (!q) {
quote = SINGLE_QUOTE;
q = strchr(statement, quote);
}
if (q) {
// Set quote to match open quote
2001-05-23 15:26:42 +02:00
quote = *q;
q++;
p = strchr(q, quote);
if (p) {
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
2001-05-23 15:26:42 +02:00
/* If we are not using the ANSI codepage then we need to
* translate the database name to ANSI from OEM.
* We will translate (p - q) characters from (q) into
* the proper offset in local_statement.
*/
if (!fAnsiCP)
OemToAnsiBuff(q, local_statement + (q - statement),
p - q);
#endif
p++;
const ptrdiff_t slen = p - statement;
2001-05-23 15:26:42 +02:00
local_statement[slen] = '\0';
if (isqlGlob.SQL_dialect == 1) {
2001-05-23 15:26:42 +02:00
if (global_usr)
sprintf(local_statement + strlen(local_statement),
" USER \'%s\' ", usr);
if (global_psw)
sprintf(local_statement + strlen(local_statement),
" PASSWORD \'%s\' ", psw);
if (*ISQL_charset && strcmp(ISQL_charset, DEFCHARSET))
sprintf(local_statement + strlen(local_statement),
" SET NAMES \'%s\' ", ISQL_charset);
sprintf(local_statement + strlen(local_statement), "%s",
p);
}
}
}
}
SLONG cnt = 0;
if ((isqlGlob.SQL_dialect == 0) || (isqlGlob.SQL_dialect > 1)) {
const TEXT* q = strchr(statement, SINGLE_QUOTE);
2001-05-23 15:26:42 +02:00
while (q) {
cnt++;
const TEXT* l = q + 1;
2001-05-23 15:26:42 +02:00
q = strchr(l, SINGLE_QUOTE);
}
TEXT* new_local_statement = NULL;
2001-05-23 15:26:42 +02:00
if (cnt > 0) {
arglength = strlen(statement) +
strlen(isqlGlob.User) + strlen(Password) + 24 + 2 * cnt;
2001-05-23 15:26:42 +02:00
if (*ISQL_charset && strcmp(ISQL_charset, DEFCHARSET))
arglength +=
strlen(ISQL_charset) + strlen(" SET NAMES \'\' ");
new_local_statement =
(TEXT*) ISQL_ALLOC(arglength + 1);
2001-05-23 15:26:42 +02:00
if (!new_local_statement)
{
ISQL_FREE(local_statement);
ISQL_FREE(usr);
ISQL_FREE(psw);
2001-05-23 15:26:42 +02:00
return (FAIL);
}
2001-05-23 15:26:42 +02:00
}
TEXT errbuf[MSG_LENGTH];
2001-05-23 15:26:42 +02:00
TEXT* temp_str;
2001-05-23 15:26:42 +02:00
if (new_local_statement)
temp_str = new_local_statement;
else
temp_str = local_statement;
const TEXT* temp_local_stmt_str = local_statement;
bool done = false;
2001-05-23 15:26:42 +02:00
while (!done) {
const TEXT* str_begin = NULL;
const TEXT* str_end = NULL;
literal_string_type str_flag = INIT_STR_FLAG;
2001-05-23 15:26:42 +02:00
get_str(temp_local_stmt_str, &str_begin, &str_end, &str_flag);
if (str_flag == INCOMPLETE_STRING) {
gds__msg_format(NULL, ISQL_MSG_FAC, INCOMPLETE_STR,
2001-05-23 15:26:42 +02:00
sizeof(errbuf), errbuf,
"create database statement",
NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
ISQL_FREE(local_statement);
ISQL_FREE(usr);
ISQL_FREE(psw);
if (new_local_statement)
ISQL_FREE(new_local_statement);
2001-05-23 15:26:42 +02:00
return (FAIL);
}
copy_str(&temp_str, &temp_local_stmt_str, &done,
str_begin, str_end, str_flag);
}
if (new_local_statement)
temp_str = new_local_statement;
else
temp_str = local_statement;
if (global_usr)
sprintf(temp_str + strlen(temp_str), " USER \'%s\' ", usr);
if (global_psw)
sprintf(temp_str + strlen(temp_str), " PASSWORD \'%s\' ", psw);
if (*ISQL_charset && strcmp(ISQL_charset, DEFCHARSET))
sprintf(temp_str + strlen(temp_str),
" SET NAMES \'%s\' ", ISQL_charset);
if (new_local_statement)
temp_str = new_local_statement + strlen(new_local_statement);
else
temp_str = local_statement + strlen(local_statement);
if (p && strlen(p) > 0) {
temp_local_stmt_str = p;
2004-05-12 21:39:17 +02:00
bool done2 = false;
while (!done2) {
const TEXT* str_begin = NULL;
const TEXT* str_end = NULL;
literal_string_type str_flag = INIT_STR_FLAG;
2001-05-23 15:26:42 +02:00
get_str(temp_local_stmt_str, &str_begin, &str_end, &str_flag);
if (str_flag == INCOMPLETE_STRING) {
gds__msg_format(NULL, ISQL_MSG_FAC, INCOMPLETE_STR,
2001-05-23 15:26:42 +02:00
sizeof(errbuf), errbuf,
"create database statement",
NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
ISQL_FREE(local_statement);
ISQL_FREE(usr);
ISQL_FREE(psw);
if (new_local_statement)
ISQL_FREE(new_local_statement);
2001-05-23 15:26:42 +02:00
return (FAIL);
}
2004-05-12 21:39:17 +02:00
copy_str(&temp_str, &temp_local_stmt_str, &done2,
2001-05-23 15:26:42 +02:00
str_begin, str_end, str_flag);
}
}
if (new_local_statement)
{
ISQL_FREE(local_statement);
2001-05-23 15:26:42 +02:00
local_statement = new_local_statement;
}
2001-05-23 15:26:42 +02:00
}
/* execute the create statement
* If the isqlGlob.SQL_dialect is not set or set to 2, create the database
2001-05-23 15:26:42 +02:00
* as a dialect 3 database.
*/
if (isqlGlob.SQL_dialect == 0 || isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION) {
2001-05-23 15:26:42 +02:00
if (isc_dsql_execute_immediate(isc_status, &DB, &M__trans, 0,
local_statement, requested_SQL_dialect,
NULL))
{
2001-05-23 15:26:42 +02:00
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))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
if (!DB)
ret = FAIL;
}
}
if (DB) {
// Load isqlGlob.global_Db_name with some value to show a successful attach
2001-05-23 15:26:42 +02:00
strip_quotes(d_name, isqlGlob.global_Db_name);
2001-05-23 15:26:42 +02:00
ISQL_get_version(true);
2001-05-23 15:26:42 +02:00
// Start the user transaction
2001-05-23 15:26:42 +02:00
if (!M__trans) {
if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL))
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
if (D__trans)
commit_trans(&D__trans);
if (isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb))
2004-05-09 07:48:33 +02:00
ISQL_errmsg(isc_status);
2001-05-23 15:26:42 +02:00
}
// Allocate a new user statement for the database
2001-05-23 15:26:42 +02:00
if (isc_dsql_allocate_statement(isc_status, &DB, &global_Stmt)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
ret = ERR;
}
}
if (local_statement)
ISQL_FREE(local_statement);
if (psw)
ISQL_FREE(psw);
if (usr)
ISQL_FREE(usr);
return (ret);
}
static void do_isql()
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d o _ i s q l
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Process incoming SQL statements, using the global sqlda
2001-05-23 15:26:42 +02:00
*
**************************************/
TEXT errbuf[MSG_LENGTH];
2001-05-23 15:26:42 +02:00
// Initialized user transactions
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
M__trans = 0;
2001-05-23 15:26:42 +02:00
// File used to edit sessions
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
Ofp = (FILE*) gds__temp_file(TRUE, SCRATCH, Tmpfile);
if (Ofp == (FILE*) - 1)
2001-12-24 03:51:06 +01:00
{
// If we can't open a temp file then bail
2001-05-23 15:26:42 +02:00
gds__msg_format(NULL, ISQL_MSG_FAC, FILE_OPEN_ERR, MSG_LENGTH,
errbuf, Tmpfile, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
Exit_value = FINI_ERROR;
exit(Exit_value);
}
// Open database and start tansaction
2001-05-23 15:26:42 +02:00
signal(SIGINT, query_abort);
2001-05-23 15:26:42 +02:00
//
// We will not excute this for now on WINDOWS. We are not prompting for
// a database name, username password. A connect statement has to be in
// the file containing the script.
//
newdb(isqlGlob.global_Db_name, isqlGlob.User, Password, global_Numbufs, isqlGlob.Role, true);
2001-05-23 15:26:42 +02:00
// If that failed or no Dbname was specified
2001-05-23 15:26:42 +02:00
ISQL_dbcheck();
2001-05-23 15:26:42 +02:00
// 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
2001-05-23 15:26:42 +02:00
global_sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(20)));
global_sqlda->version = SQLDA_VERSION1;
global_sqlda->sqln = 20;
2001-05-23 15:26:42 +02:00
// The sqldap is the handle containing the current sqlda pointer
2001-05-23 15:26:42 +02:00
global_sqldap = &global_sqlda;
2001-05-23 15:26:42 +02:00
/*
** read statements and process them from Ifp until the ret
** code tells us we are done
*/
TEXT statement[MAX_USHORT];
processing_state ret;
bool done = false;
2001-05-23 15:26:42 +02:00
while (!done) {
if (Abort_flag) {
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);
2001-05-23 15:26:42 +02:00
/*
* If there is current user statement, free it
* option 2 does the drop
*/
if (global_Stmt)
isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_drop);
2001-05-23 15:26:42 +02:00
if (DB)
isc_detach_database(isc_status, &DB);
break;
}
ret = get_statement(statement, sizeof(statement), sql_prompt);
2001-05-23 15:26:42 +02:00
// If there is no database yet, remind us of the need to connect
2001-05-23 15:26:42 +02:00
// But don't execute the statement
2001-05-23 15:26:42 +02:00
if (!isqlGlob.global_Db_name[0] && (ret == CONT)) {
2001-05-23 15:26:42 +02:00
if (!Quiet) {
gds__msg_format(NULL, ISQL_MSG_FAC, NO_DB, MSG_LENGTH,
errbuf, NULL, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
}
ret = SKIP;
}
switch (ret) {
case CONT:
2004-08-21 11:29:46 +02:00
if (process_statement(statement, global_sqldap) == ERR)
{
Exit_value = FINI_ERROR;
if (!Interactive && BailOnError)
Abort_flag = true;
}
2001-05-23 15:26:42 +02:00
break;
case END:
case EOF:
case EXIT:
if (Abort_flag) {
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);
2001-05-23 15:26:42 +02:00
}
else {
if (D__trans)
commit_trans(&D__trans);
2001-05-23 15:26:42 +02:00
if (M__trans)
commit_trans(&M__trans);
if (gds_trans)
commit_trans(&gds_trans);
2001-05-23 15:26:42 +02:00
}
/*
2001-05-23 15:26:42 +02:00
* 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);
2004-02-05 07:58:04 +01:00
if (DB) {
isc_detach_database(isc_status, &DB);
}
done = true;
2001-05-23 15:26:42 +02:00
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);
/*
2001-05-23 15:26:42 +02:00
* 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);
2004-02-05 07:58:04 +01:00
if (DB) {
isc_detach_database(isc_status, &DB);
}
done = true;
2001-05-23 15:26:42 +02:00
break;
case EXTRACT:
case EXTRACTALL:
default:
/* fb_assert (FALSE); -- removed as finds too many problems */
2001-05-23 15:26:42 +02:00
case ERR:
case FAIL:
case SKIP:
break;
}
}
2004-05-03 01:06:37 +02:00
global_Stmt = 0;
DB = 0;
isqlGlob.global_Db_name[0] = '\0';
2004-05-03 01:06:37 +02:00
D__trans = 0;
M__trans = 0;
gds_trans = 0;
2001-05-23 15:26:42 +02:00
// Should have a valid Temp file pointer
2004-04-29 00:36:29 +02:00
fb_assert(Ofp != (FILE*) - 1);
fclose(Ofp);
2001-05-23 15:26:42 +02:00
unlink(Tmpfile);
if (global_sqlda)
ISQL_FREE(global_sqlda);
2001-05-23 15:26:42 +02:00
if (Filelist)
ISQL_FREE(Filelist);
while (global_Cols) {
collist* p = global_Cols;
global_Cols = global_Cols->collist_next;
2001-05-23 15:26:42 +02:00
ISQL_FREE(p);
}
if (Exit_value == FINI_ERROR)
exit(Exit_value);
}
static processing_state drop_db()
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d r o p _ d b
*
**************************************
*
* Functional description
* Drop the current database
*
2001-05-23 15:26:42 +02:00
**************************************/
if (isqlGlob.global_Db_name[0]) {
2001-05-23 15:26:42 +02:00
if (isc_drop_database(isc_status, &DB)) {
ISQL_errmsg(isc_status);
return (FAIL);
}
// If error occured and drop database got aborted
2001-05-23 15:26:42 +02:00
if (DB)
return (FAIL);
}
else
return (FAIL);
// The database got dropped with or without errors
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
M__trans = 0;
gds_trans = 0;
global_Stmt = 0;
D__trans = 0;
// CVC: If we aren't connected to a db anymore, then the db's dialect is reset.
// This should fix SF Bug #910430.
isqlGlob.db_SQL_dialect = 0;
// BRS this is also needed to fix #910430.
dialect_spoken = 0;
2001-05-23 15:26:42 +02:00
// Zero database name
2001-05-23 15:26:42 +02:00
isqlGlob.global_Db_name[0] = '\0';
2004-05-03 01:06:37 +02:00
DB = 0;
2001-05-23 15:26:42 +02:00
return (SKIP);
}
static processing_state edit(const TEXT* const* cmd)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e d i t
*
**************************************
*
* Functional description
* Edit the current file or named file
*
* Parameters: cmd -- Array of words interpreted as file name
* The result of calling this is to point the global input file
2001-05-23 15:26:42 +02:00
* pointer, Ifp, to the named file after editing or the tmp file.
*
**************************************/
TEXT* errbuf = (TEXT*) ISQL_ALLOC((SLONG) MSG_LENGTH);
2001-05-23 15:26:42 +02:00
if (!errbuf)
return (ERR);
// Local pointer to global list of input files
2001-05-23 15:26:42 +02:00
indev* flist = Filelist;
2001-05-23 15:26:42 +02:00
// Set up editing command for shell
2001-05-23 15:26:42 +02:00
const TEXT* file = cmd[1];
2001-05-23 15:26:42 +02:00
// If there is a file name specified, try to open it
2001-05-23 15:26:42 +02:00
if (*file) {
TEXT path[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
strip_quotes(file, path);
2004-04-29 00:36:29 +02:00
FILE* fp = fopen(path, "r");
if (fp) {
// Push the current ifp on the indev
2001-05-23 15:26:42 +02:00
indev* current = (INDEV) ISQL_ALLOC((SLONG) sizeof(indev));
2001-05-23 15:26:42 +02:00
current->indev_fpointer = Ifp;
current->indev_next = NULL;
if (!Filelist)
Filelist = current;
else {
while (flist->indev_next)
flist = flist->indev_next;
flist->indev_next = current;
}
Ifp = fp;
gds__edit(file, 0);
}
else {
gds__msg_format(NULL, ISQL_MSG_FAC, FILE_OPEN_ERR, MSG_LENGTH,
errbuf, file, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
}
}
else {
// No file given, edit the temp file
2001-05-23 15:26:42 +02:00
indev* current = (INDEV) ISQL_ALLOC((SLONG) sizeof(indev));
2001-05-23 15:26:42 +02:00
current->indev_fpointer = Ifp;
current->indev_next = NULL;
if (!Filelist)
Filelist = current;
else {
while (flist->indev_next)
flist = flist->indev_next;
flist->indev_next = current;
}
// Close the file, edit it, then reopen and read from the top
2004-04-29 00:36:29 +02:00
fclose(Ofp);
2001-05-23 15:26:42 +02:00
gds__edit(Tmpfile, 0);
2004-04-29 00:36:29 +02:00
Ofp = fopen(Tmpfile, "r+");
2001-05-23 15:26:42 +02:00
Ifp = Ofp;
}
if (errbuf)
ISQL_FREE(errbuf);
return (SKIP);
}
static processing_state end_trans()
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e n d _ t r a n s
*
**************************************
*
* Functional description
* Prompt the interactive user if there is an extant transaction and
* either commit or rollback
2001-05-23 15:26:42 +02:00
*
* Called by newtrans, createdb, newdb;
* Returns success or failure.
*
**************************************/
2001-12-24 03:51:06 +01:00
TEXT infobuf[BUFFER_LENGTH60];
2001-05-23 15:26:42 +02:00
processing_state ret = CONT;
2001-05-23 15:26:42 +02:00
/* Give option of committing or rolling back before proceding unless
** the last command was a commit or rollback
2001-05-23 15:26:42 +02:00
*/
if (M__trans) {
if (Interactive) {
gds__msg_format(NULL, ISQL_MSG_FAC, COMMIT_PROMPT, sizeof(infobuf),
infobuf, NULL, NULL, NULL, NULL, NULL);
readNextInputLine(infobuf);
// ISQL_prompt(infobuf);
2004-04-29 00:36:29 +02:00
// fgets(buffer, BUFFER_LENGTH80, stdin);
if (isyesno(lastInputLine)) { // check for Yes answer
gds__msg_format(NULL, ISQL_MSG_FAC, COMMIT_MSG, sizeof(infobuf),
infobuf, NULL, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(infobuf, 1);
if (DB && M__trans) {
if (isc_commit_transaction(isc_status, &M__trans)) {
// Commit failed, so roll back anyway
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
ret = FAIL;
}
}
}
else {
gds__msg_format(NULL, ISQL_MSG_FAC, ROLLBACK_MSG,
sizeof(infobuf), infobuf, NULL, NULL, NULL,
NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(infobuf, 1);
if (DB && M__trans) {
if (isc_rollback_transaction(isc_status, &M__trans)) {
ISQL_errmsg(isc_status);
ret = FAIL;
}
}
}
2003-06-24 12:33:04 +02:00
getColumn = -1;
2001-05-23 15:26:42 +02:00
}
else { // No answer, just roll back by default
2001-05-23 15:26:42 +02:00
if (DB && M__trans) {
/* For WISQL, we keep track of whether a commit is needed by setting a flag in the ISQLPB
* structure. This flag is set whenever a sql command is entered in the SQL input area or
* if the user uses the create database dialog. Because of this, this should only show up if
* the user connects and then disconnects or if the user enters a SET TRANSACTION stat without
2001-05-23 15:26:42 +02:00
* ever doing anything that would cause changes to the dictionary.
*/
gds__msg_format(NULL, ISQL_MSG_FAC, ROLLBACK_MSG,
sizeof(infobuf), infobuf, NULL, NULL, NULL,
NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(infobuf, 1);
if (isc_rollback_transaction(isc_status, &M__trans)) {
ISQL_errmsg(isc_status);
ret = FAIL;
}
}
}
}
// Commit background transaction
2001-05-23 15:26:42 +02:00
if (DB && D__trans) {
if (isc_commit_transaction(isc_status, &D__trans)) {
ISQL_errmsg(isc_status);
ret = FAIL;
}
}
return (ret);
}
static processing_state escape(const TEXT* cmd)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e s c a p e
*
**************************************
*
* Functional description
* Permit a shell escape to system call
*
* Parameters: cmd -- The command string with SHELL
2001-05-23 15:26:42 +02:00
*
**************************************/
// Advance past the shell
2001-05-23 15:26:42 +02:00
const TEXT* shellcmd = cmd;
2001-05-23 15:26:42 +02:00
// Search past the 'shell' keyword
2001-05-23 15:26:42 +02:00
shellcmd += strlen("shell");
// eat whitespace at beginning of command
while (*shellcmd && fb_isspace(*shellcmd))
2001-05-23 15:26:42 +02:00
shellcmd++;
// If no command given just spawn a shell
2001-05-23 15:26:42 +02:00
if (!*shellcmd) {
#ifdef WIN_NT
if (system("%ComSpec%"))
return FAIL;
#else
if (system("$SHELL"))
return FAIL;
#endif
}
else {
if (system(shellcmd))
return FAIL;
}
return (SKIP);
}
static processing_state frontend(const TEXT* statement)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f r o n t e n d
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Handle any frontend commands that start with
* show or set converting the string into an
* array of words parms, with MAX_TERMS words only.
*
* Parameters: statement is the string typed by the user
2001-05-23 15:26:42 +02:00
*
**************************************/
TEXT* buffer = (TEXT*) ISQL_ALLOC((SLONG) (BUFFER_LENGTH256));
2001-05-23 15:26:42 +02:00
if (!buffer)
return (ERR);
TEXT* errbuf = (TEXT*) ISQL_ALLOC((SLONG) MSG_LENGTH);
2001-05-23 15:26:42 +02:00
if (!errbuf) {
ISQL_FREE(buffer);
return (ERR);
}
// Store the first NUM_TERMS words as they appear in parms, using blanks
// to delimit. Each word beyond a real word gets a null char
// Shift parms to upper case, leaving original case in lparms
typedef TEXT* isql_params_t[MAX_TERMS];
isql_params_t parms, lparms;
for (int iter = 0; iter < FB_NELEM(lparms); ++iter) {
lparms[iter] = NULL;
parms[iter] = NULL;
}
2001-05-23 15:26:42 +02:00
const TEXT* p = statement;
// Any whitespace and comments at the beginning are already swallowed by get_statement()
2001-05-23 15:26:42 +02:00
// Set beginning of statement past comment
const TEXT* cmd = p;
2001-05-23 15:26:42 +02:00
if (*cmd) {
for (int i = 0; i < MAX_TERMS; i++) {
bool role_found = false;
TEXT* a = buffer;
int j = 0;
if (*p == DBL_QUOTE || *p == SINGLE_QUOTE) {
if (i > 0 && (!strcmp(parms[i - 1], "ROLE")))
role_found = true;
bool delimited_done = false;
const TEXT end_quote = *p;
j++;
*a++ = *p++;
// Allow a quoted string to have embedded spaces
// Prevent overflow
while (*p && !delimited_done && j < BUFFER_LENGTH256 - 1) {
if (*p == end_quote) {
j++;
*a++ = *p++;
if (*p && *p == end_quote && j < BUFFER_LENGTH256 - 1) {
j++; // do not skip the escape quote here
*a++ = *p++;
}
else
delimited_done = true;
}
else {
j++;
*a++ = *p++;
}
}
}
else {
// Prevent overflow
while (*p && !fb_isspace(*p) && j < BUFFER_LENGTH256 - 1) {
j++;
*a++ = *p++;
}
}
*a = '\0';
const size_t length = strlen(buffer);
parms[i] = (TEXT*) ISQL_ALLOC((SLONG) (length + 1));
lparms[i] = (TEXT*) ISQL_ALLOC((SLONG) (length + 1));
strncpy(parms[i], buffer, length);
parms[i][length] = '\0';
while (*p && fb_isspace(*p))
p++;
strcpy(lparms[i], parms[i]);
if (!role_found)
ISQL_make_upper(parms[i]);
}
}
char bad_dialect_buf[512];
bool bad_dialect = false;
bool print_warning = false;
/*
** Look to see if the words (parms) match any known verbs. If nothing
** matches then just hand the statement to process_statement
*/
processing_state ret = SKIP;
if (!*cmd) {
ret = SKIP; // redundant
}
2002-06-29 15:39:11 +02:00
else if (!strcmp(parms[0], "SHOW")) {
// Transaction for all frontend commands
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
// Free the frontend command
frontend_free_parms(parms, lparms);
2001-05-23 15:26:42 +02:00
if (buffer)
ISQL_FREE(buffer);
if (errbuf)
ISQL_FREE(errbuf);
return FAIL;
}
ret = SHOW_metadata(parms, lparms);
if (gds_trans)
commit_trans(&gds_trans);
2001-05-23 15:26:42 +02:00
}
else if (!strcmp(parms[0], "ADD")) {
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
// Free the frontend command
frontend_free_parms(parms, lparms);
2001-05-23 15:26:42 +02:00
if (buffer)
ISQL_FREE(buffer);
if (errbuf)
ISQL_FREE(errbuf);
return FAIL;
}
ret = add_row(parms[1]);
if (gds_trans)
commit_trans(&gds_trans);
2001-05-23 15:26:42 +02:00
}
else if (!strcmp(parms[0], "COPY")) {
if (DB && !gds_trans)
if (isc_start_transaction(isc_status, &gds_trans, 1, &DB, 0, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
// Free the frontend command
frontend_free_parms(parms, lparms);
2001-05-23 15:26:42 +02:00
if (buffer)
ISQL_FREE(buffer);
if (errbuf)
ISQL_FREE(errbuf);
return FAIL;
}
ret = copy_table(parms[1], parms[2], lparms[3]);
if (gds_trans)
commit_trans(&gds_trans);
2001-05-23 15:26:42 +02:00
}
#ifdef MU_ISQL
/*
** This is code for QA Test bed Multiuser environment.
*/
else if (!strcmp(parms[0], "PAUSE")) {
if (qa_mu_environment()) {
qa_mu_pause();
ret = SKIP;
}
else
ret = CONT;
}
#endif // MU_ISQL
else if ((!strcmp(parms[0], "BLOBVIEW")) || (!strcmp(parms[0], "BLOBDUMP")))
ret = blobedit(parms[0], lparms);
2001-05-23 15:26:42 +02:00
else if ((!strcmp(parms[0], "OUTPUT")) || (!strcmp(parms[0], "OUT")))
ret = newoutput(lparms[1]);
else if (!strcmp(parms[0], "SHELL"))
ret = escape(cmd);
else if (!strcmp(parms[0], "SET"))
{
// Display current set options
2001-05-23 15:26:42 +02:00
if (!*parms[1])
ret = print_sets();
else if ((!strcmp(parms[1], "STATS")) || (!strcmp(parms[1], "STAT")))
ret = do_set_command(parms[2], &Stats);
else if (!strcmp(parms[1], "COUNT"))
ret = do_set_command(parms[2], &Docount);
else if (!strcmp(parms[1], "LIST"))
ret = do_set_command(parms[2], &List);
else if (!strcmp(parms[1], "PLAN"))
ret = do_set_command(parms[2], &Plan);
else if (!strcmp (parms[1], "PLANONLY")) {
2001-05-23 15:26:42 +02:00
ret = do_set_command (parms[2], &Planonly);
2002-06-29 15:39:11 +02:00
if ( Planonly && (! Plan) ) {
// turn on plan
2001-05-23 15:26:42 +02:00
ret = do_set_command ("ON", &Plan);
}
}
else if ((!strcmp(parms[1], "BLOBDISPLAY")) ||
(!strcmp(parms[1], "BLOB")))
{
// No arg means turn off blob display
2001-05-23 15:26:42 +02:00
if (!*parms[2] || !strcmp(parms[2], "OFF"))
Doblob = NO_BLOBS;
else if (!strcmp(parms[2], "ALL"))
Doblob = ALL_BLOBS;
else
Doblob = atoi(parms[2]);
}
else if (!strcmp(parms[1], "ECHO")) {
2001-05-23 15:26:42 +02:00
ret = do_set_command(parms[2], &Echo);
if (!Echo)
ISQL_prompt("");
}
2001-05-23 15:26:42 +02:00
else if ((!strcmp(parms[1], "AUTODDL")) ||
(!strcmp(parms[1], "AUTO")))
{
2001-05-23 15:26:42 +02:00
ret = do_set_command(parms[2], &Autocommit);
}
2002-06-29 15:39:11 +02:00
#ifdef SCROLLABLE_CURSORS
2001-05-23 15:26:42 +02:00
else if (!strcmp(parms[1], "AUTOFETCH"))
ret = do_set_command(parms[2], &Autofetch);
2002-06-29 15:39:11 +02:00
#endif
2001-05-23 15:26:42 +02:00
else if (!strcmp(parms[1], "WIDTH"))
ret = newsize(parms[2], parms[3]);
else if ((!strcmp(parms[1], "TRANSACTION")) ||
(!strcmp(parms[1], "TRANS")))
{
2001-05-23 15:26:42 +02:00
ret = newtrans(cmd);
}
2001-05-23 15:26:42 +02:00
else if ((!strcmp(parms[1], "TERM")) ||
(!strcmp(parms[1], "TERMINATOR")))
{
const TEXT* a = (*lparms[2]) ? lparms[2] : DEFTERM;
Termlen = strlen(a);
if (Termlen <= MAXTERM_LENGTH)
2001-05-23 15:26:42 +02:00
{
strcpy(isqlGlob.global_Term, a);
2001-05-23 15:26:42 +02:00
}
else
{
Termlen = MAXTERM_LENGTH;
strncpy(isqlGlob.global_Term, a, MAXTERM_LENGTH);
2001-05-23 15:26:42 +02:00
}
}
else if (!strcmp(parms[1], "NAMES"))
{
if (!*parms[2])
{
const size_t lgth = strlen(DEFCHARSET);
2001-05-23 15:26:42 +02:00
if (lgth <= MAXCHARSET_LENGTH)
strcpy(ISQL_charset, DEFCHARSET);
else
strncpy(ISQL_charset, DEFCHARSET, MAXCHARSET_LENGTH);
}
else
{
const size_t lgth = strlen(parms[2]);
2001-05-23 15:26:42 +02:00
if (lgth <= MAXCHARSET_LENGTH)
strcpy(ISQL_charset, parms[2]);
else
strncpy(ISQL_charset, parms[2], MAXCHARSET_LENGTH);
}
}
else if (!strcmp(parms[1], "TIME"))
{
ret = do_set_command(parms[2], &Time_display);
}
#ifdef DEV_BUILD
else if (!strcmp(parms[1], "SQLDA_DISPLAY"))
{
ret = do_set_command(parms[2], &Sqlda_display);
}
#endif // DEV_BUILD
2001-05-23 15:26:42 +02:00
else if (!strcmp(parms[1], "SQL"))
{
if (!strcmp(parms[2], "DIALECT"))
{
const USHORT old_SQL_dialect = isqlGlob.SQL_dialect; // save the old SQL dialect
const SCHAR* dialect_str = parms[3];
if (dialect_str && (isqlGlob.SQL_dialect = atoi(dialect_str)))
2001-05-23 15:26:42 +02:00
{
if (isqlGlob.SQL_dialect < SQL_DIALECT_V5 ||
isqlGlob.SQL_dialect > SQL_DIALECT_V6)
2001-05-23 15:26:42 +02:00
{
bad_dialect = true;
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf, "%s%s",
"invalid SQL dialect ", dialect_str);
isqlGlob.SQL_dialect = old_SQL_dialect; // restore SQL dialect
2001-05-23 15:26:42 +02:00
ret = ERR;
}
else
{
if (isqlGlob.major_ods)
2001-05-23 15:26:42 +02:00
{
if (isqlGlob.major_ods < ODS_VERSION10)
2001-05-23 15:26:42 +02:00
{
if (isqlGlob.SQL_dialect > SQL_DIALECT_V5)
2001-05-23 15:26:42 +02:00
{
if (dialect_spoken) {
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf,
"%s%d%s%s%s%d%s",
"ERROR: Database SQL dialect ",
dialect_spoken,
" database does not accept Client SQL dialect ",
dialect_str,
" setting. Client SQL dialect still remains ",
old_SQL_dialect, NEWLINE);
}
else {
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf,
"%s%s%s%s%s%s",
"ERROR: Pre IB V6 database only speaks ",
"Database SQL dialect 1 and ",
"does not accept Client SQL dialect ",
dialect_str,
" setting. Client SQL dialect still remains 1.",
NEWLINE);
}
isqlGlob.SQL_dialect = old_SQL_dialect; // restore SQL dialect
ISQL_printf(isqlGlob.Out, bad_dialect_buf);
2001-05-23 15:26:42 +02:00
}
}
else
{ // ODS 10 databases
2001-05-23 15:26:42 +02:00
switch (dialect_spoken)
{
case SQL_DIALECT_V5:
if (isqlGlob.SQL_dialect > SQL_DIALECT_V5)
2001-05-23 15:26:42 +02:00
{
if (SQL_DIALECT_V6_TRANSITION)
Merge_stderr = true;
print_warning = true;
2001-05-23 15:26:42 +02:00
}
break;
case SQL_DIALECT_V6:
if (isqlGlob.SQL_dialect == SQL_DIALECT_V5 ||
isqlGlob.SQL_dialect ==
2001-05-23 15:26:42 +02:00
SQL_DIALECT_V6_TRANSITION)
{
if (SQL_DIALECT_V6_TRANSITION)
Merge_stderr = true;
print_warning = true;
2001-05-23 15:26:42 +02:00
}
break;
default:
break;
}
if (print_warning && Warnings)
{
print_warning = false;
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf, "%s%d%s%d%s%s",
"WARNING: Client SQL dialect has been set to ",
isqlGlob.SQL_dialect,
2001-05-23 15:26:42 +02:00
" when connecting to Database SQL dialect ",
dialect_spoken,
" database. ", NEWLINE);
ISQL_printf(isqlGlob.Out, bad_dialect_buf);
2001-05-23 15:26:42 +02:00
}
}
}
}
}
else
{ // handle non numeric invalid "set sql dialect" case
isqlGlob.SQL_dialect = old_SQL_dialect; // restore SQL dialect
bad_dialect = true;
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf, "%s%s", "invalid SQL dialect ",
dialect_str);
ret = ERR;
}
}
else
ret = ERR;
}
else if (!strcmp (parms[1], "WARNINGS") || !strcmp (parms[1], "WNG")) {
ret = do_set_command (parms[2], &Warnings);
}
2001-05-23 15:26:42 +02:00
else if (!strcmp(parms[1], "GENERATOR"))
ret = CONT;
else if (!strcmp(parms[1], "STATISTICS"))
ret = CONT;
else if (!strcmp(parms[1], "HEADING"))
ret = do_set_command(parms[2], &Heading);
else if (!strcmp(parms[1], "BAIL"))
ret = do_set_command(parms[2], &BailOnError);
2001-05-23 15:26:42 +02:00
else
ret = ERR;
}
else if (!strcmp(parms[0], "CREATE"))
{
if (!strcmp(parms[1], "DATABASE") || !strcmp(parms[1], "SCHEMA"))
ret = create_db(cmd, lparms[2]);
else
ret = CONT;
}
else if (!strcmp(parms[0], "DROP"))
{
if (!strcmp(parms[1], "DATABASE") || !strcmp(parms[1], "SCHEMA"))
if (*parms[2])
ret = ERR;
else
ret = drop_db();
else
ret = CONT;
}
else if (!strcmp(parms[0], "CONNECT"))
{
const TEXT* psw = NULL;
const TEXT* usr = NULL;
const TEXT* sql_role_nm = NULL;
const TEXT* numbufs = NULL;
2001-05-23 15:26:42 +02:00
/* if a parameter is given in the command more than once, the
last one will be used. The parameters can appear each any
order, but each must provide a value. */
ret = SKIP;
for (int i = 2; i < (MAX_TERMS - 1);)
2001-05-23 15:26:42 +02:00
{
if (!strcmp(parms[i], "CACHE") && *lparms[i + 1])
{
numbufs = lparms[i + 1];
i += 2;
}
else if (!strcmp(parms[i], "USER") && *lparms[i + 1])
{
usr = lparms[i + 1];
i += 2;
}
else if (!strcmp(parms[i], "PASSWORD") && *lparms[i + 1])
{
psw = lparms[i + 1];
i += 2;
}
else if (!strcmp(parms[i], "ROLE") && *lparms[i + 1])
{
sql_role_nm = lparms[i + 1];
i += 2;
}
else if (*parms[i])
{
// Unrecognized option to CONNECT
2001-05-23 15:26:42 +02:00
ret = ERR;
break;
}
else
i++;
}
if (ret != ERR)
ret = newdb(lparms[1], usr, psw, numbufs, sql_role_nm, true);
2001-05-23 15:26:42 +02:00
}
#ifdef SCROLLABLE_CURSORS
// perform scrolling within the currently active cursor
2001-05-23 15:26:42 +02:00
else if (!strcmp(parms[0], "NEXT")) {
fetch_offset = 1;
fetch_direction = 0;
ret = FETCH;
}
else if (!strcmp(parms[0], "PRIOR")) {
fetch_offset = 1;
fetch_direction = 1;
ret = FETCH;
}
else if (!strcmp(parms[0], "FIRST")) {
fetch_offset = 1;
fetch_direction = 2;
ret = FETCH;
}
else if (!strcmp(parms[0], "LAST")) {
fetch_offset = 1;
fetch_direction = 3;
ret = FETCH;
}
else if (!strcmp(parms[0], "ABSOLUTE")) {
fetch_direction = 4;
fetch_offset = atoi(parms[1]);
ret = FETCH;
}
else if (!strcmp(parms[0], "RELATIVE")) {
fetch_direction = 5;
fetch_offset = atoi(parms[1]);
ret = FETCH;
}
#endif
else if (!strcmp(parms[0], "EDIT"))
ret = edit(lparms);
else if ((!strcmp(parms[0], "INPUT")) || (!strcmp(parms[0], "IN"))) {
Input_file = true;
2001-05-23 15:26:42 +02:00
ret = input(lparms[1]);
}
else if (!strcmp(parms[0], "QUIT"))
ret = BACKOUT;
else if (!strcmp(parms[0], "EXIT"))
ret = EXIT;
else if (!strcmp(parms[0], "?") || (!strcmp(parms[0], "HELP")))
ret = help(parms[1]);
else // Didn't match, it must be SQL
2001-05-23 15:26:42 +02:00
ret = CONT;
// In case any default transaction was started - commit it here
if (gds_trans)
commit_trans(&gds_trans);
2001-05-23 15:26:42 +02:00
// Free the frontend command
frontend_free_parms(parms, lparms);
2001-05-23 15:26:42 +02:00
if (ret == ERR) {
2003-09-09 13:03:37 +02:00
if (bad_dialect)
gds__msg_format(NULL, ISQL_MSG_FAC, CMD_ERR, MSG_LENGTH,
errbuf, bad_dialect_buf, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
else
gds__msg_format(NULL, ISQL_MSG_FAC, CMD_ERR, MSG_LENGTH,
errbuf, cmd, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
}
if (buffer)
ISQL_FREE(buffer);
if (errbuf)
ISQL_FREE(errbuf);
return (ret);
}
static void frontend_free_parms(TEXT* parms[], TEXT* lparms[])
{
for (int j = 0; j < MAX_TERMS; j++) {
if (parms[j]) {
ISQL_FREE(parms[j]);
ISQL_FREE(lparms[j]);
}
}
}
static processing_state do_set_command(const TEXT* parm,
bool* global_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d o _ s e t _ c o m m a n d
*
**************************************
*
* Functional description
* set the flag pointed to by global_flag
* to true or false.
* if parm is missing, toggle it
* if parm is "ON", set it to true
* if parm is "OFF", set it to false
2001-05-23 15:26:42 +02:00
*
**************************************/
processing_state ret = SKIP;
2001-05-23 15:26:42 +02:00
if (!*parm)
*global_flag = !*global_flag;
2001-05-23 15:26:42 +02:00
else if (!strcmp(parm, "ON"))
*global_flag = true;
2001-05-23 15:26:42 +02:00
else if (!strcmp(parm, "OFF"))
*global_flag = false;
2001-05-23 15:26:42 +02:00
else
ret = ERR;
return (ret);
}
static processing_state get_statement(TEXT* const statement,
USHORT bufsize,
const TEXT* statement_prompt)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ s t a t e m e n t
*
**************************************
*
* Functional description
* Get an SQL statement, or QUIT/EXIT command to process
*
* Arguments: Pointer to statement, and size of statement_buffer
2001-05-23 15:26:42 +02:00
*
**************************************/
processing_state ret = CONT;
2001-05-23 15:26:42 +02:00
INDEV flist, oflist;
// Lookup the continuation prompt once
TEXT con_prompt[MSG_LENGTH];
2001-05-23 15:26:42 +02:00
ISQL_msg_get(CON_PROMPT, con_prompt, NULL, NULL, NULL, NULL, NULL);
if (Interactive || Echo) {
ISQL_prompt(statement_prompt);
2004-04-29 00:36:29 +02:00
// fprintf(stdout, statement_prompt);
// fflush(stdout);
2001-05-23 15:26:42 +02:00
}
// Clear out statement buffer
2001-05-23 15:26:42 +02:00
TEXT* p = statement;
2001-05-23 15:26:42 +02:00
*p = '\0';
// Set count of characters to zero
2001-05-23 15:26:42 +02:00
int count = 0;
int valuable_count = 0; // counter of valuable (non-space) chars
2004-08-27 07:00:31 +02:00
const int term_length = Termlen - 1; // additional variable for decreasing calculation
bool done = false;
2001-05-23 15:26:42 +02:00
enum {normal, in_single_line_comment, in_comment,
in_single_quoted_string, in_double_quoted_string} state = normal;
2001-05-23 15:26:42 +02:00
while (!done) {
SSHORT c = getNextInputChar();
switch (c) {
2001-05-23 15:26:42 +02:00
case EOF:
2004-04-29 00:36:29 +02:00
// Go back to getc if we get interrupted by a signal.
2001-05-23 15:26:42 +02:00
if (SYSCALL_INTERRUPTED(errno)) {
errno = 0;
break;
}
// If there was something valuable before EOF - error
if (valuable_count > 0) {
TEXT errbuf[MSG_LENGTH];
gds__msg_format(NULL, ISQL_MSG_FAC, UNEXPECTED_EOF, MSG_LENGTH,
errbuf, NULL, NULL, NULL, NULL, NULL);
STDERROUT(errbuf, 1);
Exit_value = FINI_ERROR;
}
// If we hit EOF at the top of the flist, exit time
2001-05-23 15:26:42 +02:00
flist = Filelist;
if (!flist->indev_next)
return FOUND_EOF;
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
// If this is not tmpfile, close it
2001-05-23 15:26:42 +02:00
if (Ifp != Ofp)
2004-04-29 00:36:29 +02:00
fclose(Ifp);
2001-05-23 15:26:42 +02:00
// Save the last flist so it can be deleted
2001-05-23 15:26:42 +02:00
while (flist->indev_next) {
oflist = flist;
flist = flist->indev_next;
}
// Zero out last pointer
2001-05-23 15:26:42 +02:00
oflist->indev_next = NULL;
// Reset to previous after other input
2001-05-23 15:26:42 +02:00
Ifp = flist->indev_fpointer;
ISQL_FREE(flist);
if (Interactive || Echo)
2001-05-23 15:26:42 +02:00
ISQL_prompt(statement_prompt);
// Try to convince the new routines to go back to previous file(s)
// This should fix the INPUT bug introduced with editline.
getColumn = -1;
2001-05-23 15:26:42 +02:00
break;
case '\n':
// case '\0': // In particular with readline the \n is removed
if (state == in_single_line_comment)
{
state = normal;
2001-05-23 15:26:42 +02:00
}
/* Catch the help ? without a terminator */
if (*statement == '?' && count == 1) {
c = 0;
done = true;
2001-05-23 15:26:42 +02:00
break;
}
// If in a comment, keep reading
if (Interactive || Echo)
{
if (state == in_comment)
{ // Comment prompt
ISQL_prompt("--> ");
}
else if (valuable_count == 0)
{ // Ignore a series of nothing at the beginning
ISQL_prompt(statement_prompt);
}
else
{
2001-05-23 15:26:42 +02:00
ISQL_prompt(con_prompt);
}
2001-05-23 15:26:42 +02:00
}
break;
2001-05-23 15:26:42 +02:00
case '-':
// Could this the be start of a single-line comment.
if (state == normal && count > 0 && (*(p - 1) == '-'))
{
state = in_single_line_comment;
if (valuable_count == 1)
valuable_count = 0;
2001-05-23 15:26:42 +02:00
}
break;
case '*':
/* Could this the be start of a comment. We can only look back,
** not forward
* ignore possibilities of a comment beginning inside
* quoted strings.
*/
if (state == normal && count > 0 && (*(p - 1) == '/'))
{
state = in_comment;
if (valuable_count == 1)
valuable_count = 0;
}
2001-05-23 15:26:42 +02:00
break;
case '/':
/* Perhaps this is the end of a comment
2001-05-23 15:26:42 +02:00
* ignore possibilities of a comment ending inside
* quoted strings.
*/
if (state == in_comment && (*(p - 1) == '*'))
{
state = normal;
valuable_count--; // This char is not valuable
}
2001-05-23 15:26:42 +02:00
break;
case SINGLE_QUOTE:
switch (state)
{
2004-08-27 07:13:47 +02:00
case normal:
state = in_single_quoted_string;
break;
case in_single_quoted_string:
state = normal;
break;
}
2001-05-23 15:26:42 +02:00
break;
case DBL_QUOTE:
switch (state)
{
2004-08-27 07:13:47 +02:00
case normal:
state = in_double_quoted_string;
break;
case in_double_quoted_string:
state = normal;
break;
}
2001-05-23 15:26:42 +02:00
break;
default:
if (state == normal && c == isqlGlob.global_Term[term_length] &&
// one-char terminator or the beginning also match
(Termlen == 1 ||
(valuable_count >= term_length &&
2004-08-27 07:00:31 +02:00
strncmp(p - term_length, isqlGlob.global_Term, term_length) == 0)))
{
c = 0;
done = true;
p -= term_length;
count -= term_length;
}
}
2001-05-23 15:26:42 +02:00
// Any non-space character is significant if not in comment
if (state != in_comment &&
state != in_single_line_comment &&
!fb_isspace(c) && c != EOF)
{
valuable_count++;
if (valuable_count == 1) // this is the first valuable char in stream
{ // ignore all previous crap
p = statement;
count = 0;
}
2001-05-23 15:26:42 +02:00
}
*p++ = c;
count++;
2001-05-23 15:26:42 +02:00
/* If in danger of running out of room, reallocate to a bigger
2001-05-23 15:26:42 +02:00
** Statement buffer, keeping track of where p is pointing
*/
if (count > bufsize && !done)
{
TEXT* errbuf = (TEXT*) ISQL_ALLOC((SLONG) MSG_LENGTH);
2001-05-23 15:26:42 +02:00
if (errbuf) {
gds__msg_format(NULL, ISQL_MSG_FAC, BUFFER_OVERFLOW,
2004-08-27 07:00:31 +02:00
MSG_LENGTH, errbuf, NULL, NULL,
NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
ISQL_FREE(errbuf);
}
ret = SKIP;
// move some content to start of buffer just in case if
// the overflow has splitter terminator
count = MAX(Termlen, 2);
2004-08-27 07:00:31 +02:00
memcpy(statement, p - count, count);
p = statement + count;
2001-05-23 15:26:42 +02:00
*p = '\0';
}
}
// If this was a null statement, skip it
2001-05-23 15:26:42 +02:00
if (!*statement)
ret = SKIP;
if (ret == CONT)
ret = frontend(statement);
2001-05-23 15:26:42 +02:00
if (ret == CONT) {
/* Place each non frontend statement in the temp file if we are reading
2004-04-29 00:36:29 +02:00
** from stdin. Add newline to make the file more readable
2001-05-23 15:26:42 +02:00
*/
2004-04-29 00:36:29 +02:00
if (Ifp == stdin) {
fputs(statement, Ofp);
fputs(isqlGlob.global_Term, Ofp);
2004-04-29 00:36:29 +02:00
fputc('\n', Ofp);
2001-05-23 15:26:42 +02:00
}
}
return ret;
}
static void get_str(const TEXT* const input_str,
const TEXT** str_begin,
const TEXT** str_end,
2003-09-09 13:03:37 +02:00
literal_string_type* str_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ s t r
*
**************************************
*
* Functional description
*
* Scanning a string constant from the input buffer. If it found, then
* passes back the starting address and ending address of the string
2001-05-23 15:26:42 +02:00
* constant. It also marks the type of string constant and passes back.
*
2001-05-23 15:26:42 +02:00
* string type:
*
* 1. SINGLE_QUOTED_STRING --- string constant delimited by
* single quotes
* 2. DOUBLE_QUOTED_STRING --- string constant delimited by
* double quotes
* 3. NO_MORE_STRING --- no string constant was found
2001-05-23 15:26:42 +02:00
*
* 4. INCOMPLETE_STRING --- no matching ending quote
2003-09-09 13:03:37 +02:00
*
2001-05-23 15:26:42 +02:00
* Input Arguments:
*
* 1. input buffer
*
* Output Arguments:
*
* 1. pointer to the input buffer
* 2. address to the begining of a string constant
* 3. address to the ending of a string constant
* 4. address to the string flag
*
**************************************/
const TEXT* b1 = strchr(input_str, SINGLE_QUOTE);
const TEXT* b2 = strchr(input_str, DBL_QUOTE);
2001-05-23 15:26:42 +02:00
if (!b1 && !b2)
*str_flag = NO_MORE_STRING;
else {
if (b1 && !b2)
*str_flag = SINGLE_QUOTED_STRING;
else if (!b1 && b2) {
*str_flag = DOUBLE_QUOTED_STRING;
b1 = b2;
}
else if (b1 > b2) {
*str_flag = DOUBLE_QUOTED_STRING;
b1 = b2;
}
else
*str_flag = SINGLE_QUOTED_STRING;
*str_begin = b1;
2003-09-09 13:03:37 +02:00
TEXT delimited_char = *b1;
2001-05-23 15:26:42 +02:00
b1++;
2003-09-09 13:03:37 +02:00
bool done = false;
2001-05-23 15:26:42 +02:00
while (!done) {
if (*b1 == delimited_char) {
b1++;
if (*b1 == delimited_char)
b1++;
else {
done = true;
2001-05-23 15:26:42 +02:00
*str_end = b1;
}
}
else
b1 = strchr(b1, delimited_char);
/* In case there is no matching ending quote (single or double)
either because of mismatched quotes or simply because user
2001-05-23 15:26:42 +02:00
didn't complete the quotes, we cannot find a valid string
at all. So we return a special value INCOMPLETE_STRING.
In this case str_end is not populated and hence copy_str
should not be called after get_str, but instead the caller
must process this error & return to it's calling routine
- Shailesh */
if (b1 == NULL) {
*str_flag = INCOMPLETE_STRING;
done = true;
2001-05-23 15:26:42 +02:00
}
2004-03-07 08:58:55 +01:00
} // end of while (!done)
2001-05-23 15:26:42 +02:00
}
}
void ISQL_get_version(bool call_by_create_db)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ g e t _ v e r s i o n
*
**************************************
*
* Functional description
* finds out if the database we just attached to is
* V4 or newer as well as other info.
2001-05-23 15:26:42 +02:00
*
**************************************/
static const UCHAR db_version_info[] = {
isc_info_ods_version,
2004-08-27 07:00:31 +02:00
//isc_info_ods_minor_version,
isc_info_db_sql_dialect,
isc_info_firebird_version,
isc_info_end
2001-05-23 15:26:42 +02:00
};
/*
** Each info item requested will return
2001-05-23 15:26:42 +02:00
**
** 1 byte for the info item tag
** 2 bytes for the length of the information that follows
** 1 to 4 bytes of integer information
2001-05-23 15:26:42 +02:00
**
** isc_info_end will not have a 2-byte length - which gives us
** some padding in the buffer.
*/
2002-06-29 15:39:11 +02:00
// UCHAR buffer[sizeof(db_version_info) * (1 + 2 + 4)];
// Now we are also getting the Firebird server version which is a
// string the above calculation does not apply. NM 03-Oct-2001
2004-09-05 17:00:51 +02:00
UCHAR buffer[BUFFER_LENGTH512];
char bad_dialect_buf[BUFFER_LENGTH512];
bool print_warning = false;
2001-05-23 15:26:42 +02:00
dialect_spoken = 0;
if (isc_database_info(isc_status, &DB, sizeof(db_version_info),
reinterpret_cast<const char*>(db_version_info),
sizeof(buffer), (SCHAR*) buffer))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return;
}
const UCHAR* p = buffer;
2004-09-05 17:00:51 +02:00
while (*p != isc_info_end && *p != isc_info_truncated) {
const UCHAR item = (UCHAR) *p++;
const USHORT length = gds__vax_integer(p, sizeof(USHORT));
2001-05-23 15:26:42 +02:00
p += sizeof(USHORT);
switch (item) {
case isc_info_ods_version:
isqlGlob.major_ods = gds__vax_integer(p, length);
2001-05-23 15:26:42 +02:00
break;
2004-08-27 07:00:31 +02:00
//case isc_info_ods_minor_version:
// minor_ods = gds__vax_integer(p, length);
// break;
2001-05-23 15:26:42 +02:00
case isc_info_db_sql_dialect:
dialect_spoken = gds__vax_integer(p, length);
if (isqlGlob.major_ods < ODS_VERSION10) {
if (isqlGlob.SQL_dialect > SQL_DIALECT_V5 && Warnings) {
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf, "%s%s%s%d%s%s",
"WARNING: Pre IB V6 database only speaks",
" SQL dialect 1 and ",
"does not accept Client SQL dialect ",
isqlGlob.SQL_dialect,
2001-05-23 15:26:42 +02:00
" . Client SQL dialect is reset to 1.", NEWLINE);
ISQL_printf(isqlGlob.Out, bad_dialect_buf);
2001-05-23 15:26:42 +02:00
}
}
else { // ODS 10 databases
2001-05-23 15:26:42 +02:00
switch (dialect_spoken) {
case SQL_DIALECT_V5:
if (isqlGlob.SQL_dialect > SQL_DIALECT_V5)
print_warning = true;
2001-05-23 15:26:42 +02:00
break;
case SQL_DIALECT_V6:
if (isqlGlob.SQL_dialect != 0 && isqlGlob.SQL_dialect < SQL_DIALECT_V6)
print_warning = true;
2001-05-23 15:26:42 +02:00
break;
default:
break;
}
if (print_warning && Warnings) {
print_warning = false;
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf, "%s%d%s%d%s%s",
"WARNING: This database speaks SQL dialect ",
dialect_spoken,
" but Client SQL dialect was set to ",
isqlGlob.SQL_dialect, " .", NEWLINE);
ISQL_printf(isqlGlob.Out, bad_dialect_buf);
2001-05-23 15:26:42 +02:00
}
}
break;
case isc_info_error:
/*
2001-05-23 15:26:42 +02:00
** Error indicates an option was not understood by the
** remote server.
*/
if (*p == isc_info_firebird_version) {
// must be an old or non Firebird server
break;
}
if (isqlGlob.SQL_dialect && isqlGlob.SQL_dialect != SQL_DIALECT_V5 && Warnings) {
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
if (call_by_create_db)
sprintf(bad_dialect_buf, "%s%s%d%s%s",
"WARNING: Pre IB V6 server only speaks SQL dialect 1",
" and does not accept Client SQL dialect ",
isqlGlob.SQL_dialect,
2001-05-23 15:26:42 +02:00
" . Client SQL dialect is reset to 1.", NEWLINE);
else {
connecting_to_pre_v6_server = true;
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf, "%s%s%d%s%s",
"ERROR: Pre IB V6 server only speaks SQL dialect 1",
" and does not accept Client SQL dialect ",
isqlGlob.SQL_dialect,
2001-05-23 15:26:42 +02:00
" . Client SQL dialect is reset to 1.", NEWLINE);
}
ISQL_printf(isqlGlob.Out, bad_dialect_buf);
2001-05-23 15:26:42 +02:00
}
else {
if (isqlGlob.SQL_dialect == 0) {
connecting_to_pre_v6_server = true;
2001-05-23 15:26:42 +02:00
sprintf(bad_dialect_buf, "%s%s%d%s%s",
"ERROR: Pre IB V6 server only speaks SQL dialect 1",
" and does not accept Client SQL dialect ",
isqlGlob.SQL_dialect,
2001-05-23 15:26:42 +02:00
" . Client SQL dialect is reset to 1.", NEWLINE);
ISQL_printf(isqlGlob.Out, bad_dialect_buf);
2001-05-23 15:26:42 +02:00
}
}
break;
case isc_info_firebird_version:
strcpy(server_version, "Server: ");
strncat(server_version, (char*) &(p[2]), (length - 2));
if (Version_info) {
strcat(server_version, NEWLINE);
ISQL_printf (isqlGlob.Out, server_version);
}
break;
2002-06-29 15:39:11 +02:00
2001-05-23 15:26:42 +02:00
default:
{
TEXT message[100];
sprintf(message,
"Internal error: Unexpected isc_info_value %d\n",
item);
ISQL_printf(isqlGlob.Out, message);
2001-05-23 15:26:42 +02:00
}
break;
}
p += length;
}
if (isqlGlob.major_ods < ODS_VERSION8) {
TEXT* errbuf = (TEXT*) ISQL_ALLOC((SLONG) MSG_LENGTH);
if (errbuf) {
gds__msg_format(NULL, ISQL_MSG_FAC, SERVER_TOO_OLD, MSG_LENGTH,
errbuf, NULL, NULL, NULL, NULL, NULL);
STDERROUT(errbuf, 1);
ISQL_FREE(errbuf);
}
return;
2001-05-23 15:26:42 +02:00
}
/* If the remote server did not respond to our request for
"dialects spoken", then we can assume it can only speak
the V5 dialect. We automatically change the connection
dialect to that spoken by the server. Otherwise the
2001-05-23 15:26:42 +02:00
current dialect is set to whatever the user requested. */
if (dialect_spoken == 0)
isqlGlob.SQL_dialect = SQL_DIALECT_V5;
else if (isqlGlob.major_ods < ODS_VERSION10)
isqlGlob.SQL_dialect = dialect_spoken;
else if (isqlGlob.SQL_dialect == 0) // client SQL dialect has not been set
isqlGlob.SQL_dialect = dialect_spoken;
2001-05-23 15:26:42 +02:00
if (dialect_spoken > 0)
isqlGlob.db_SQL_dialect = dialect_spoken;
2001-05-23 15:26:42 +02:00
else
isqlGlob.db_SQL_dialect = SQL_DIALECT_V5;
2001-05-23 15:26:42 +02:00
}
void ISQL_remove_and_unescape_quotes(TEXT* string, const char quote)
{
/**************************************
*
* I S Q L _ r e m o v e _ a n d _ u n e s c a p e _ q u o t e s
*
**************************************
*
* Functional description
* Remove the delimited quotes. Blanks could be part of
* delimited SQL identifier. It has to deal with embedded quotes, too.
*
**************************************/
const size_t cmd_len = strlen(string);
TEXT* q = string;
const TEXT* p = q;
const TEXT* const end_of_str = p + cmd_len;
for (size_t cnt = 1; cnt < cmd_len && p < end_of_str; cnt++) {
p++;
if (cnt < cmd_len - 1) {
*q = *p;
if (p + 1 < end_of_str) {
if (*(p + 1) == quote) // skip the escape double quote
p++;
}
else {
p++;
*q = '\0';
}
}
else {
*q = '\0';
}
q++;
}
*q = '\0';
}
void ISQL_truncate_term(TEXT* str,
USHORT len)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ t r u n c a t e _ t e r m
*
**************************************
*
* Functional description
* Truncates the rightmost contiguous spaces on a string.
* CVC: Notice isspace may be influenced by locales.
2001-05-23 15:26:42 +02:00
**************************************/
int i;
for (i = len - 1; i >= 0 && ((fb_isspace(str[i])) || (str[i] == 0)); i--);
2001-05-23 15:26:42 +02:00
str[i + 1] = 0;
}
2003-10-16 10:51:06 +02:00
void ISQL_ri_action_print(const TEXT* ri_action_str,
const TEXT* ri_action_prefix_str,
bool all_caps)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* I S Q L _ r i _ a c t i o n _ p r i n t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* prints the description of ref. integrity actions.
* The actions must be one of the cascading actions or RESTRICT.
* RESTRICT is used to indicate that the user did not specify any
* actions, so do not print it out.
2001-05-23 15:26:42 +02:00
*
**************************************/
2003-10-16 10:51:06 +02:00
for (const ri_actions* ref_int = ri_actions_all; ref_int->ri_action_name;
++ref_int)
{
2001-05-23 15:26:42 +02:00
if (!strcmp(ref_int->ri_action_name, ri_action_str)) {
if (*ref_int->ri_action_print_caps)
{
// we have something to print
2001-05-23 15:26:42 +02:00
if (all_caps)
sprintf(Print_buffer, "%s %s", ri_action_prefix_str,
ref_int->ri_action_print_caps);
else if (*ref_int->ri_action_print_mixed)
sprintf(Print_buffer, "%s %s", ri_action_prefix_str,
ref_int->ri_action_print_mixed);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
return;
}
}
fb_assert(FALSE);
2001-05-23 15:26:42 +02:00
}
static bool get_numeric(const UCHAR* string,
USHORT length,
SSHORT* scale,
SINT64* ptr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ n u m e r i c
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Convert a numeric literal (string) to its binary value.
2001-05-23 15:26:42 +02:00
*
* The binary value (int64) is stored at the
* address given by ptr.
2001-05-23 15:26:42 +02:00
*
**************************************/
SINT64 value = 0;
SSHORT local_scale = 0, sign = 0;
bool digit_seen = false, fraction = false;
2001-05-23 15:26:42 +02:00
const UCHAR* const end = string + length;
for (const UCHAR* p = string; p < end; p++) {
2001-05-23 15:26:42 +02:00
if (DIGIT(*p)) {
digit_seen = true;
2001-05-23 15:26:42 +02:00
/* Before computing the next value, make sure there will be
no overflow. Trying to detect overflow after the fact is
tricky: the value doesn't always become negative after an
overflow! */
if (value >= INT64_LIMIT) {
// possibility of an overflow
2001-05-23 15:26:42 +02:00
if (value > INT64_LIMIT)
break;
else if (((*p > '8') && (sign == -1))
|| ((*p > '7') && (sign != -1)))
{
2001-05-23 15:26:42 +02:00
break;
}
2001-05-23 15:26:42 +02:00
}
/* Force the subtraction to be performed before the addition,
thus preventing a possible signed arithmetic overflow. */
2001-05-23 15:26:42 +02:00
value = value * 10 + (*p - '0');
if (fraction)
--local_scale;
}
else if (*p == '.') {
if (fraction)
return false;
2001-05-23 15:26:42 +02:00
else
fraction = true;
2001-05-23 15:26:42 +02:00
}
else if (*p == '-' && !digit_seen && !sign && !fraction)
sign = -1;
else if (*p == '+' && !digit_seen && !sign && !fraction)
sign = 1;
else if (*p != BLANK)
return false;
2001-05-23 15:26:42 +02:00
}
if (!digit_seen)
return false;
2001-05-23 15:26:42 +02:00
*scale = local_scale;
*(SINT64*) ptr = ((sign == -1) ? -value : value);
return true;
2001-05-23 15:26:42 +02:00
}
static processing_state print_sets()
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r i n t _ s e t s
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Print the current set values
*
**************************************/
ISQL_printf(isqlGlob.Out, "Print statistics: ");
ISQL_printf(isqlGlob.Out, (Stats ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, "Echo commands: ");
ISQL_printf(isqlGlob.Out, (Echo ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, "List format: ");
ISQL_printf(isqlGlob.Out, (List ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, "Row Count: ");
ISQL_printf(isqlGlob.Out, (Docount ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, "Autocommit DDL: ");
ISQL_printf(isqlGlob.Out, (Autocommit ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
#ifdef SCROLLABLE_CURSORS
ISQL_printf(isqlGlob.Out, "Autofetch records: ");
ISQL_printf(isqlGlob.Out, (Autofetch ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
#endif
ISQL_printf(isqlGlob.Out, "Access Plan: ");
ISQL_printf(isqlGlob.Out, (Plan ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, "Access Plan only: ");
ISQL_printf(isqlGlob.Out, (Planonly ? "ON" : "OFF"));
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, "Display BLOB type: ");
2001-05-23 15:26:42 +02:00
if (Doblob == ALL_BLOBS)
ISQL_printf(isqlGlob.Out, "ALL");
2001-05-23 15:26:42 +02:00
else if (Doblob == NO_BLOBS)
ISQL_printf(isqlGlob.Out, "NONE");
2001-05-23 15:26:42 +02:00
else {
sprintf(Print_buffer, "%d", Doblob);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
if (*ISQL_charset && strcmp(ISQL_charset, DEFCHARSET)) {
sprintf(Print_buffer, "Set names: %s%s",
2001-05-23 15:26:42 +02:00
ISQL_charset, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
ISQL_printf(isqlGlob.Out, "Column headings: ");
ISQL_printf(isqlGlob.Out, Heading ? "ON" : "OFF");
ISQL_printf(isqlGlob.Out, NEWLINE);
if (global_Cols) {
2004-08-30 12:07:49 +02:00
const collist* p = global_Cols;
2001-05-23 15:26:42 +02:00
sprintf(Print_buffer, "Column print widths:%s", NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
while (p) {
sprintf(Print_buffer, "%s%s width: %d%s",
TAB_AS_SPACES, p->col_name, p->col_len, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
p = p->collist_next;
}
}
ISQL_printf(isqlGlob.Out, "Terminator: ");
ISQL_printf(isqlGlob.Out, isqlGlob.global_Term);
ISQL_printf(isqlGlob.Out, NEWLINE);
sprintf(Print_buffer, "Time: %s%s",
2001-05-23 15:26:42 +02:00
(Time_display) ? "ON" : "OFF", NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
sprintf(Print_buffer, "Warnings: %s%s",
2001-05-23 15:26:42 +02:00
(Warnings) ? "ON" : "OFF", NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
sprintf(Print_buffer, "Bail on error: %s%s",
(BailOnError) ? "ON" : "OFF", NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
return (SKIP);
}
static processing_state help(const TEXT* what)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* h e l p
*
**************************************
*
* Functional description
* List the known commands.
*
**************************************/
/* Ordered list of help messages to display. Use -1 to terminate list,
* and 0 for an empty blank line
*/
static const SSHORT help_ids[] = {
HLP_FRONTEND, // Frontend commands:
2001-05-23 15:26:42 +02:00
HLP_BLOBDMP, /*BLOBDUMP <blobid> <file> -- dump BLOB to a file */
HLP_BLOBVIEW, /*BLOBVIEW <blobid> -- view BLOB in text editor */
HLP_EDIT, /*EDIT [<filename>] -- edit SQL script file and execute */
HLP_EDIT2, /*EDIT -- edit current command buffer and execute */
HLP_HELP, /*HELP -- display this menu */
HLP_INPUT, /*INput <filename> -- take input from the named SQL file */
HLP_OUTPUT, /*OUTput [<filename>] -- write output to named file */
2004-04-29 00:36:29 +02:00
HLP_OUTPUT2, /*OUTput -- return output to stdout */
HLP_SET_ROOT, /*SET <option> -- (use HELP SET for list) */
HLP_SHELL, /*SHELL <command> -- execute Operating System command in sub-shell */
HLP_SHOW, /*SHOW <object> [<name>] -- display system information */
HLP_OBJTYPE, /* <object> = CHECK, DATABASE, DOMAIN, EXCEPTION, FILTER, FUNCTION, GENERATOR, */
HLP_OBJTYPE2, /* GRANT, INDEX, PROCEDURE, ROLE, SQL DIALECT, SYSTEM, TABLE, */
HLP_OBJTYPE3, /* TRIGGER, VERSION, VIEW */
HLP_EXIT, /*EXIT -- exit and commit changes */
HLP_QUIT, /*QUIT -- exit and roll back changes */
2001-05-23 15:26:42 +02:00
0,
HLP_ALL, // All commands may be abbreviated to letters in CAPitals
-1 // end of list
2001-05-23 15:26:42 +02:00
};
static const SSHORT help_set_ids[] = {
HLP_SETCOM, //Set commands:
HLP_SET, /* SET -- display current SET options */
HLP_SETAUTO, /* SET AUTOddl -- toggle autocommit of DDL statements */
2001-05-23 15:26:42 +02:00
#ifdef SCROLLABLE_CURSORS
HLP_SETFETCH, /* SET AUTOfetch -- toggle autofetch of records */
2001-05-23 15:26:42 +02:00
#endif
HLP_SETBAIL, /* SET BAIL -- toggle bailing out on errors in non-interactive mode */
HLP_SETBLOB, /* SET BLOB [ALL|<n>] -- display BLOBS of subtype <n> or ALL */
HLP_SETBLOB2, /* SET BLOB -- turn off BLOB display */
HLP_SETCOUNT, /* SET COUNT -- toggle count of selected rows on/off */
HLP_SETECHO, /* SET ECHO -- toggle command echo on/off */
HLP_SETHEADING, // SET HEADING -- toggle column titles display on/off
HLP_SETLIST, /* SET LIST -- toggle column or table display format */
HLP_SETNAMES, /* SET NAMES <csname> -- set name of runtime character set */
HLP_SETPLAN, /* SET PLAN -- toggle display of query access plan */
HLP_SETPLANONLY, /* SET PLANONLY -- toggle display of query plan without executing*/
2001-05-23 15:26:42 +02:00
HLP_SETSQLDIALECT, /* SET SQL DIALECT <n> -- set sql dialect to <n> */
HLP_SETSTAT, /* SET STATs -- toggle display of performance statistics */
HLP_SETTIME, /* SET TIME -- toggle display of timestamp with DATE values */
HLP_SETTERM, /* SET TERM <string> -- change statement terminator string */
HLP_SETWIDTH, /* SET WIDTH <col> [<n>] -- set/unset print width to <n> for column <col> */
2001-05-23 15:26:42 +02:00
0,
HLP_ALL, // All commands may be abbreviated to letters in CAPitals
-1 // end of list
2001-05-23 15:26:42 +02:00
};
TEXT msg[MSG_LENGTH];
const SSHORT* msgid;
if (!strcmp(what, "SET")) {
2001-05-23 15:26:42 +02:00
msgid = help_set_ids;
}
else {
2001-05-23 15:26:42 +02:00
msgid = help_ids;
}
2001-05-23 15:26:42 +02:00
for (; *msgid != -1; msgid++) {
if (*msgid != 0) {
ISQL_msg_get(*msgid, msg, NULL, NULL, NULL, NULL, NULL);
ISQL_printf(Help, msg);
}
ISQL_printf(Help, NEWLINE);
2001-05-23 15:26:42 +02:00
}
return (SKIP);
}
static processing_state input(const TEXT* infile)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n p u t
*
**************************************
*
* Functional description
* Read commands from the named input file
*
2001-05-23 15:26:42 +02:00
* Parameters: infile -- Second word of the command line
* filelist is a stack of file pointers to
* return from nested inputs
*
* The result of calling this is to point the
* global input file pointer, Ifp, to the named file.
2001-05-23 15:26:42 +02:00
*
**************************************/
TEXT* errbuf = (TEXT*) ISQL_ALLOC(MSG_LENGTH);
2001-05-23 15:26:42 +02:00
if (!errbuf)
return (ERR);
// Local pointer to global list of input files
indev* flist = Filelist;
// Set up editing command for shell
const TEXT* file = infile;
2001-05-23 15:26:42 +02:00
// If there is no file name specified, return error
2001-05-23 15:26:42 +02:00
if (!file || !*file) {
if (errbuf)
ISQL_FREE(errbuf);
return (ERR);
}
TEXT path[MAXPATHLEN];
strip_quotes(infile, path);
2001-05-23 15:26:42 +02:00
file = path;
/* filelist is a linked list of file pointers. We must add a node to
** the linked list before discarding the current Ifp.
2001-05-23 15:26:42 +02:00
** Filelist is a global pointing to base of list.
*/
2004-04-29 00:36:29 +02:00
FILE* fp = fopen(file, "r");
if (fp) {
indev* current = (INDEV) ISQL_ALLOC((SLONG) sizeof(indev));
2001-05-23 15:26:42 +02:00
current->indev_fpointer = Ifp;
current->indev_next = NULL;
#ifdef DEBUG_GDS_ALLOC
gds_alloc_flag_unfreed((void*) current);
2001-05-23 15:26:42 +02:00
#endif
if (!Filelist)
Filelist = current;
else {
while (flist->indev_next)
flist = flist->indev_next;
flist->indev_next = current;
}
Ifp = fp;
}
else {
gds__msg_format(NULL, ISQL_MSG_FAC, FILE_OPEN_ERR, MSG_LENGTH,
errbuf, file, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
ISQL_FREE(errbuf);
return FAIL;
}
if (errbuf)
ISQL_FREE(errbuf);
Input_file = true;
2001-05-23 15:26:42 +02:00
return (SKIP);
}
static bool isyesno(const TEXT* buffer)
2001-05-23 15:26:42 +02:00
{
/**********************************************
*
* i s y e s n o
*
**********************************************
*
* Functional description
* check if the first letter of the user's response
* corresponds to the first letter of Yes
* (in whatever language they are using)
*
* returns true for Yes, otherwise false.
2001-05-23 15:26:42 +02:00
*
**********************************************/
if (!have_trans) { // get the translation if we don't have it already
2001-05-23 15:26:42 +02:00
ISQL_msg_get(YES_ANS, yesword, NULL, NULL, NULL, NULL, NULL);
have_trans = true;
2001-05-23 15:26:42 +02:00
}
// Just check first byte of yes response -- could be multibyte problem
2001-05-23 15:26:42 +02:00
if (UPPER7(buffer[0]) == UPPER7(yesword[0]))
return true;
2001-05-23 15:26:42 +02:00
else
return false;
2001-05-23 15:26:42 +02:00
}
static processing_state newdb(TEXT* dbname,
const TEXT* usr,
const TEXT* psw,
const TEXT* numbufs,
const TEXT* sql_role_nm,
bool start_user_trans)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e w d b
*
**************************************
*
* Functional description
* Change the current database from what it was.
* This procedure is called when we first enter this program.
*
* Parameters: dbname -- Name of database to open
* usr -- user name, if given
* psw -- password, if given
* numbufs -- # of connection cache buffers, if given
2001-05-23 15:26:42 +02:00
* sql_role_nm -- sql role name
*
2001-05-23 15:26:42 +02:00
**************************************/
// No name of a database, just return an error
2001-05-23 15:26:42 +02:00
if (!dbname || !*dbname)
return ERR;
/*
2001-05-23 15:26:42 +02:00
* Since the dbname is set already, in the case where a database is specified
* on the command line, we need to save it so ISQL_disconnect does not NULL it
* out. We will restore it after the disconnect. The save_database buffer
* will also be used to translate dbname to the proper character set.
*/
SCHAR* save_database = (SCHAR*) ISQL_ALLOC(strlen(dbname) + 1);
2001-05-23 15:26:42 +02:00
if (!save_database)
return (ERR);
strcpy(save_database, dbname);
ISQL_disconnect_database(false);
2001-05-23 15:26:42 +02:00
strcpy(dbname, save_database);
TEXT* errbuf = (TEXT*) ISQL_ALLOC(MSG_LENGTH);
if (!errbuf) {
ISQL_FREE(save_database);
2001-05-23 15:26:42 +02:00
return ERR;
}
TEXT* local_psw = (TEXT*) ISQL_ALLOC(BUFFER_LENGTH128);
2001-05-23 15:26:42 +02:00
if (!local_psw) {
ISQL_FREE(save_database);
ISQL_FREE(errbuf);
return ERR;
}
TEXT* local_usr = (TEXT*) ISQL_ALLOC(BUFFER_LENGTH128);
2001-05-23 15:26:42 +02:00
if (!local_usr) {
ISQL_FREE(save_database);
ISQL_FREE(errbuf);
ISQL_FREE(local_psw);
return ERR;
}
TEXT* local_sql_role = (TEXT*) ISQL_ALLOC(BUFFER_LENGTH256);
2001-05-23 15:26:42 +02:00
if (!local_sql_role) {
ISQL_FREE(save_database);
ISQL_FREE(errbuf);
ISQL_FREE(local_psw);
ISQL_FREE(local_usr);
return ERR;
}
#ifdef DEBUG_GDS_ALLOC
gds_alloc_flag_unfreed((void*) errbuf);
gds_alloc_flag_unfreed((void*) local_psw);
gds_alloc_flag_unfreed((void*) local_usr);
gds_alloc_flag_unfreed((void*) local_sql_role);
2001-05-23 15:26:42 +02:00
#endif
// global user and passwords are set only if they are not originally set
2001-05-23 15:26:42 +02:00
local_psw[0] = 0;
local_usr[0] = 0;
local_sql_role[0] = 0;
TEXT local_numbufs[BUFFER_LENGTH128];
2001-05-23 15:26:42 +02:00
local_numbufs[0] = '\0';
// Strip quotes if well-intentioned
2001-05-23 15:26:42 +02:00
strip_quotes(dbname, isqlGlob.global_Db_name);
2001-05-23 15:26:42 +02:00
strip_quotes(usr, local_usr);
strip_quotes(psw, local_psw);
strip_quotes(numbufs, local_numbufs);
/* if local user is not specified, see if global options are
specified - don't let a global role setting carry forward if a
specific local user was specified */
/* Special handling for SQL Roles:
* V5 client -- strip the quotes
* V6 client -- pass the role name as it is
2001-05-23 15:26:42 +02:00
*/
if (sql_role_nm) {
if (isqlGlob.SQL_dialect == SQL_DIALECT_V5)
2001-05-23 15:26:42 +02:00
strip_quotes(sql_role_nm, local_sql_role);
else
strcpy(local_sql_role, sql_role_nm);
}
if (!(strlen(local_sql_role)) && global_role) {
if (isqlGlob.SQL_dialect == SQL_DIALECT_V5)
strip_quotes(isqlGlob.Role, local_sql_role);
2001-05-23 15:26:42 +02:00
else
strcpy(local_sql_role, isqlGlob.Role);
2001-05-23 15:26:42 +02:00
}
if (!(strlen(local_usr)) && global_usr)
strcpy(local_usr, isqlGlob.User);
2001-05-23 15:26:42 +02:00
if (!(strlen(local_psw)) && global_psw)
strcpy(local_psw, Password);
if (!(strlen(local_numbufs)) && has_global_numbufs)
strcpy(local_numbufs, global_Numbufs);
2001-05-23 15:26:42 +02:00
// Build up a dpb
Firebird::ClumpletWriter dpb(true, MAX_DPB_SIZE, isc_dpb_version1);
2001-05-23 15:26:42 +02:00
if (*ISQL_charset && strcmp(ISQL_charset, DEFCHARSET)) {
dpb.insertString(isc_dpb_lc_ctype, ISQL_charset, strlen(ISQL_charset));
2001-05-23 15:26:42 +02:00
}
SSHORT l;
2001-05-23 15:26:42 +02:00
if (l = strlen(local_usr)) {
dpb.insertString(isc_dpb_user_name, local_usr, l);
2001-05-23 15:26:42 +02:00
}
if (l = strlen(local_psw)) {
dpb.insertString(isc_dpb_password, local_psw, l);
2001-05-23 15:26:42 +02:00
}
if (l = strlen(local_sql_role)) {
dpb.insertInt(isc_dpb_sql_dialect, isqlGlob.SQL_dialect);
dpb.insertString(isc_dpb_sql_role_name, local_sql_role, l);
2001-05-23 15:26:42 +02:00
}
if (l = strlen(local_numbufs)) {
dpb.insertInt(isc_dpb_num_buffers, atoi(local_numbufs));
2001-05-23 15:26:42 +02:00
}
TEXT* local_name = isqlGlob.global_Db_name;
2001-05-23 15:26:42 +02:00
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
2001-05-23 15:26:42 +02:00
if (!fAnsiCP) {
local_name = save_database;
OemToAnsi(isqlGlob.global_Db_name, local_name);
2001-05-23 15:26:42 +02:00
}
#endif
if (isc_attach_database
(isc_status, 0, local_name, &DB, dpb.getBufferLength(),
reinterpret_cast<const char*>(dpb.getBuffer())))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
isqlGlob.global_Db_name[0] = '\0';
2001-05-23 15:26:42 +02:00
if (save_database)
ISQL_FREE(save_database);
if (local_psw)
ISQL_FREE(local_psw);
if (local_usr)
ISQL_FREE(local_usr);
if (local_sql_role)
ISQL_FREE(local_sql_role);
if (errbuf)
ISQL_FREE(errbuf);
return FAIL;
}
ISQL_get_version(false);
2001-05-23 15:26:42 +02:00
if (*local_sql_role) {
switch (isqlGlob.SQL_dialect) {
2001-05-23 15:26:42 +02:00
case SQL_DIALECT_V5:
// Uppercase the Sql isqlGlob.Role name
ISQL_make_upper(local_sql_role);
2001-05-23 15:26:42 +02:00
break;
case SQL_DIALECT_V6_TRANSITION:
case SQL_DIALECT_V6:
if (*local_sql_role == DBL_QUOTE
|| *local_sql_role == SINGLE_QUOTE)
{
// Remove the delimited quotes and escape quote from ROLE name.
const TEXT end_quote = *local_sql_role;
ISQL_remove_and_unescape_quotes(local_sql_role, end_quote);
2001-05-23 15:26:42 +02:00
}
else {
ISQL_make_upper(local_sql_role);
2001-05-23 15:26:42 +02:00
}
break;
default:
break;
}
}
// CVC: Do not put the user and pw used to extract metadata in a script!
// Only acknowledge user connection parameters in interactive logins.
if (Interactive) {
if (local_usr[0] != '\0') {
if (local_sql_role[0] != '\0') {
2004-07-21 12:34:27 +02:00
sprintf(Print_buffer, "Database: %s, User: %s, Role: %s%s",
dbname, local_usr, local_sql_role, NEWLINE);
}
else {
2004-07-21 12:34:27 +02:00
sprintf(Print_buffer, "Database: %s, User: %s%s",
dbname, local_usr, NEWLINE);
}
ISQL_printf(isqlGlob.Out, Print_buffer);
}
else {
if (local_sql_role[0] != '\0') {
2004-07-21 12:34:27 +02:00
sprintf(Print_buffer, "Database: %s, Role: %s%s",
dbname, local_sql_role, NEWLINE);
}
else {
sprintf(Print_buffer, "Database: %s%s", dbname, NEWLINE);
}
ISQL_printf(isqlGlob.Out, Print_buffer);
}
}
2001-05-23 15:26:42 +02:00
/* CVC: We do not require those pesky transactions for -x or -a options.
Metadata extraction works exclusively with default transaction gds__trans. */
2001-05-23 15:26:42 +02:00
if (start_user_trans) {
2001-05-23 15:26:42 +02:00
// Start the user transaction and default transaction
2001-05-23 15:26:42 +02:00
if (!M__trans) {
if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL))
ISQL_errmsg(isc_status);
if (D__trans)
commit_trans(&D__trans);
if (isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb))
ISQL_errmsg(isc_status);
}
2001-05-23 15:26:42 +02:00
2002-06-29 15:39:11 +02:00
// 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';
2004-05-03 01:06:37 +02:00
M__trans = 0;
D__trans = 0;
if (save_database)
ISQL_FREE(save_database);
if (local_psw)
ISQL_FREE(local_psw);
if (local_usr)
ISQL_FREE(local_usr);
if (local_sql_role)
ISQL_FREE(local_sql_role);
if (errbuf)
ISQL_FREE(errbuf);
return FAIL;
}
}
else {
global_Stmt = 0;
}
2001-05-23 15:26:42 +02:00
if (save_database)
ISQL_FREE(save_database);
if (local_psw)
ISQL_FREE(local_psw);
if (local_usr)
ISQL_FREE(local_usr);
if (local_sql_role)
ISQL_FREE(local_sql_role);
if (errbuf)
ISQL_FREE(errbuf);
return (SKIP);
}
static processing_state newoutput(const TEXT* outfile)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e w o u t p u t
*
**************************************
*
* Functional description
* Change the current output file
2001-05-23 15:26:42 +02:00
*
* Parameters: outfile : Name of file to receive query output
*
**************************************/
processing_state ret = SKIP;
// If there is a file name, attempt to open it for append
2001-05-23 15:26:42 +02:00
if (*outfile) {
TEXT path[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
strip_quotes(outfile, path);
outfile = path;
2004-04-29 00:36:29 +02:00
FILE* fp = fopen(outfile, "a");
if (fp)
2001-05-23 15:26:42 +02:00
{
if (isqlGlob.Out && isqlGlob.Out != stdout)
fclose(isqlGlob.Out);
isqlGlob.Out = fp;
2001-05-23 15:26:42 +02:00
if (Merge_stderr)
isqlGlob.Errfp = isqlGlob.Out;
if (Merge_diagnostic)
Diag = isqlGlob.Out;
2001-05-23 15:26:42 +02:00
}
else {
TEXT* errbuf = (TEXT*) ISQL_ALLOC(MSG_LENGTH);
2001-05-23 15:26:42 +02:00
if (errbuf) {
gds__msg_format(NULL, ISQL_MSG_FAC, FILE_OPEN_ERR, MSG_LENGTH,
errbuf, outfile, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
ISQL_FREE(errbuf);
}
ret = FAIL;
}
}
else {
2004-04-29 00:36:29 +02:00
// Revert to stdout
if (isqlGlob.Out != stdout) {
fclose(isqlGlob.Out);
isqlGlob.Out = stdout;
2001-05-23 15:26:42 +02:00
if (Merge_stderr)
isqlGlob.Errfp = isqlGlob.Out;
if (Merge_diagnostic)
Diag = isqlGlob.Out;
2001-05-23 15:26:42 +02:00
}
}
return (ret);
}
static processing_state newsize(const TEXT* colname,
const TEXT* sizestr)
{
/**************************************
*
* n e w s i z e
*
**************************************
*
* Functional description
* Add a column name and print width to collist
*
**************************************/
collist* p = global_Cols;
collist* pold = NULL;
if (!*colname || (strlen(colname) >= WORDLENGTH))
return ERR;
// If no size is given, remove the entry
if (!*sizestr) {
// Scan until name matches or end of list
while (p && strcmp(p->col_name, colname)) {
pold = p;
p = p->collist_next;
}
// If there is a match on name, delete the entry
if (p) {
if (pold)
pold->collist_next = p->collist_next;
else
global_Cols = NULL;
ISQL_FREE(p);
}
return SKIP;
}
const SSHORT size = atoi(sizestr);
if (size <= 0)
return ERR;
collist* c = (collist*) ISQL_ALLOC((SLONG) sizeof(collist));
strcpy(c->col_name, colname);
c->col_len = size;
c->collist_next = NULL;
// walk the linked list
if (!p)
global_Cols = c;
else {
while (p->collist_next && strcmp(p->col_name, colname))
p = p->collist_next;
// If there is a match on name, replace the length
if (!strcmp(p->col_name, colname)) {
p->col_len = size;
ISQL_FREE(c);
}
else
p->collist_next = c;
}
return SKIP;
}
static processing_state newtrans(const TEXT* statement)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e w t r a n s
*
**************************************
*
* Functional description
* Intercept and handle a set transaction statement by zeroing M__trans
*
* Leave the default transaction, gds_trans, alone
2001-05-23 15:26:42 +02:00
* Parameters: The statement in its entirety
*
**************************************/
if (!ISQL_dbcheck())
return FAIL;
if (end_trans())
return FAIL;
2004-05-03 01:06:37 +02:00
M__trans = 0;
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
// M__trans = 0 after the commit or rollback. ready for a new transaction
2001-05-23 15:26:42 +02:00
if (isc_dsql_execute_immediate(isc_status, &DB, &M__trans, 0, statement,
isqlGlob.SQL_dialect, NULL))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return FAIL;
}
return SKIP;
}
static processing_state parse_arg(int argc,
SCHAR** argv,
SCHAR* tabname,
2004-04-29 00:36:29 +02:00
FILE** sess)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a r s e _ a r g
*
**************************************
*
* Functional description
* Parse command line flags
2001-05-23 15:26:42 +02:00
* All flags except database are -X. Look for
* the - and make it so.
*
**************************************/
processing_state ret = SKIP;
TEXT* errbuf = (TEXT*) ISQL_ALLOC(MSG_LENGTH);
if (!errbuf) {
2001-05-23 15:26:42 +02:00
return (ERR);
}
2001-05-23 15:26:42 +02:00
// Initialize database name
2001-05-23 15:26:42 +02:00
*isqlGlob.global_Db_name = '\0';
*isqlGlob.global_Target_db = '\0';
2001-05-23 15:26:42 +02:00
Password[0] = '\0';
isqlGlob.User[0] = '\0';
isqlGlob.Role[0] = '\0';
global_Numbufs[0] = 0;
Quiet = false;
2001-05-23 15:26:42 +02:00
Exit_value = FINI_OK;
/*
** default behavior in V6.0 is SQL_DIALECT_V6
2001-05-23 15:26:42 +02:00
*/
requested_SQL_dialect = SQL_DIALECT_V6;
Merge_stderr = false;
Merge_diagnostic = false;
2001-05-23 15:26:42 +02:00
2003-02-11 21:17:56 +01:00
#if defined (WIN95)
2001-05-23 15:26:42 +02:00
#define STRARGCPY(dest, src) fAnsiCP ? strcpy(dest, src) : AnsiToOem(src, dest)
#define STRARGNCPY(dest, src, len) fAnsiCP ? strncpy(dest, src, len) : AnsiToOemBuff(src, dest, len)
#else
#define STRARGCPY strcpy
#define STRARGNCPY strncpy
#endif
{ // scope
const size_t lgth = strlen(DEFCHARSET);
if (lgth <= MAXCHARSET_LENGTH)
strcpy(ISQL_charset, DEFCHARSET);
else
strncpy(ISQL_charset, DEFCHARSET, MAXCHARSET_LENGTH);
} // scope
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
// redirected stdin means act like -i was set
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
Ifp = stdin;
2001-05-23 15:26:42 +02:00
// Terminators are initialized
2001-05-23 15:26:42 +02:00
Termlen = strlen(DEFTERM);
if (Termlen <= MAXTERM_LENGTH)
strcpy(isqlGlob.global_Term, DEFTERM);
2001-05-23 15:26:42 +02:00
else {
Termlen = MAXTERM_LENGTH;
strncpy(isqlGlob.global_Term, DEFTERM, MAXTERM_LENGTH);
2001-05-23 15:26:42 +02:00
}
// Initialize list of input file pointers
2001-05-23 15:26:42 +02:00
Filelist = 0;
// Interpret each command line argument
const SCHAR switchchar = '-';
#ifdef DEV_BUILD
bool istable = false;
#endif
2004-12-09 03:50:47 +01:00
int i = 1;
const char* s = argv[i];
2001-05-23 15:26:42 +02:00
while (i < argc) {
/* Look at flags to find unique match. If the flag has an arg,
** advance the pointer (and i). Only change the return value
** for extract switch or error.
*/
if (*s == switchchar) {
const int c = UPPER(s[1]);
2001-05-23 15:26:42 +02:00
if (c == 'X' || (c == 'E' && UPPER(s[2]) == 'X')) {
#ifdef DEV_BUILD
if (UPPER(s[2]) == 'T')
istable = true;
2001-05-23 15:26:42 +02:00
#endif
ret = EXTRACT;
}
else if (c == 'A')
ret = EXTRACTALL;
else if (c == 'B')
BailOnError = true;
2001-05-23 15:26:42 +02:00
else if (c == 'E')
Echo = true;
2004-11-24 10:04:06 +01:00
else if (c == 'M' && s[2] == '2') // It must be before single M option.
Merge_diagnostic = true;
2001-05-23 15:26:42 +02:00
else if (c == 'M')
Merge_stderr = true;
2001-05-23 15:26:42 +02:00
else if (c == 'N' && UPPER(s[2]) == 'O' && UPPER(s[3]) == 'W')
Warnings = false;
2001-05-23 15:26:42 +02:00
else if (c == 'N' &&
(!s[2] || (UPPER(s[2]) == 'O' && !s[3]) ||
(UPPER(s[2]) == 'O' && UPPER(s[3]) == 'A')))
{
Autocommit = false;
}
2001-05-23 15:26:42 +02:00
else if (c == 'O') {
// Alternate output file
2001-05-23 15:26:42 +02:00
if ((s = argv[++i]) && (newoutput(s) != FAIL))
;
else
ret = ERR;
}
else if (c == 'I') {
// Alternate input file
2001-05-23 15:26:42 +02:00
if ((s = argv[++i]) && (input(s) != FAIL))
;
2001-05-23 15:26:42 +02:00
else
ret = ERR;
Interactive = false;
Input_file = true;
2001-05-23 15:26:42 +02:00
}
else if (c == 'T') {
// The next string is the proposed terminator
2001-05-23 15:26:42 +02:00
if ((s = argv[++i]) && *s) {
Termlen = strlen(s);
if (Termlen <= MAXTERM_LENGTH)
STRARGCPY(isqlGlob.global_Term, s);
2001-05-23 15:26:42 +02:00
else {
Termlen = MAXTERM_LENGTH;
STRARGNCPY(isqlGlob.global_Term, s, MAXTERM_LENGTH);
2001-05-23 15:26:42 +02:00
}
}
else
ret = ERR;
}
else if (c == 'D') {
// advance to the next word for target db name
2001-05-23 15:26:42 +02:00
if ((s = argv[++i]) && *s)
STRARGCPY(isqlGlob.global_Target_db, s);
2001-05-23 15:26:42 +02:00
else
ret = ERR;
}
else if (c == 'P' && UPPER(s[2]) == 'A' && UPPER(s[3]) == 'G') {
// Change the page length
2001-05-23 15:26:42 +02:00
if ((s = argv[++i]) && (Pagelength = atoi(s)));
else
ret = ERR;
}
// This has to be after "PAGE"
2001-05-23 15:26:42 +02:00
else if (c == 'P') {
if ((s = argv[++i]) && *s) {
STRARGCPY(Password, s);
global_psw = true;
2001-05-23 15:26:42 +02:00
}
else
ret = ERR;
}
else if (c == 'U') {
if ((s = argv[++i]) && *s) {
STRARGCPY(isqlGlob.User, s);
global_usr = true;
2001-05-23 15:26:42 +02:00
}
else
ret = ERR;
}
else if (c == 'R') {
if ((s = argv[++i]) && *s) {
STRARGCPY(isqlGlob.Role, s);
global_role = true;
2001-05-23 15:26:42 +02:00
}
else
ret = ERR;
}
// number of cache buffers
else if (c == 'C'
&& (!s[2] || UPPER(s[2])== 'A'))
{
2001-05-23 15:26:42 +02:00
if ((s = argv[++i]) && *s) {
STRARGCPY(global_Numbufs, s);
has_global_numbufs = true;
2001-05-23 15:26:42 +02:00
}
else
ret = ERR;
}
// conection character set
else if (c == 'C' && UPPER(s[2]) == 'H') {
if ((s = argv[++i]) && *s) {
STRARGCPY(Charset, s);
const size_t lgth = strlen(Charset);
if (lgth <= MAXCHARSET_LENGTH)
strcpy(ISQL_charset, Charset);
else
strncpy(ISQL_charset, Charset, MAXCHARSET_LENGTH);
}
else
ret = ERR;
}
2001-05-23 15:26:42 +02:00
else if (c == 'Q')
// Quiet flag
Quiet = true;
2001-05-23 15:26:42 +02:00
else if (c == 'Z') {
Version_info = true;
gds__msg_format(NULL, ISQL_MSG_FAC, VERSION, MSG_LENGTH,
errbuf, FB_VERSION, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
sprintf(Print_buffer, "%s%s", errbuf, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
else if (c == 'S') {
// isqlGlob.SQL_dialect value
2001-05-23 15:26:42 +02:00
if ((s = argv[++i]) && (requested_SQL_dialect = atoi(s))) {
if (requested_SQL_dialect < SQL_DIALECT_V5 ||
requested_SQL_dialect > SQL_DIALECT_CURRENT)
{
2001-05-23 15:26:42 +02:00
ret = ERR;
}
2001-05-23 15:26:42 +02:00
else {
/* requested_SQL_dialect is used to specify the database dialect
* if SQL dialect was not specified. Since it is possible to
* have a client dialect of 2, force the database dialect to 3, but
* leave the client dialect as 2
*/
isqlGlob.SQL_dialect = requested_SQL_dialect;
if (requested_SQL_dialect == SQL_DIALECT_V6_TRANSITION)
{
Merge_stderr = true;
2001-05-23 15:26:42 +02:00
requested_SQL_dialect = SQL_DIALECT_V6;
}
}
}
else
ret = ERR;
}
else { // This is an unknown flag
2001-05-23 15:26:42 +02:00
gds__msg_format(NULL, ISQL_MSG_FAC, SWITCH, MSG_LENGTH,
errbuf, s + 1, NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
ret = ERR;
}
}
else
// This is not a switch, it is a db_name
2001-05-23 15:26:42 +02:00
{
STRARGCPY(isqlGlob.global_Db_name, s);
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
// If there is a table name, it follows
2001-05-23 15:26:42 +02:00
if (istable && (s = argv[++i]) && *s)
STRARGCPY(tabname, s);
#endif
}
// Quit the loop of interpreting if we got an error
2001-05-23 15:26:42 +02:00
if (ret == ERR)
break;
s = argv[++i];
}
// If not input, then set up first filelist
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
if (Ifp == stdin) {
2003-09-17 12:49:29 +02:00
Filelist = (INDEV) ISQL_ALLOC((SLONG) sizeof(indev));
2001-05-23 15:26:42 +02:00
Filelist->indev_fpointer = Ifp;
Filelist->indev_next = NULL;
#ifdef DEBUG_GDS_ALLOC
gds_alloc_flag_unfreed((void*) Filelist);
2001-05-23 15:26:42 +02:00
#endif
}
if (errbuf)
ISQL_FREE(errbuf);
return (ret);
#undef STRARGCPY
#undef STRARGNCPY
}
static SSHORT print_item(TEXT** s,
XSQLVAR* var,
SLONG printlength)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r i n t _ i t e m
*
**************************************
*
* Functional description
* Print an SQL data item as described.
*
**************************************/
2004-06-06 07:25:32 +02:00
TEXT d[32];
2003-09-17 12:49:29 +02:00
tm times;
2001-05-23 15:26:42 +02:00
TEXT* p = *s;
2001-05-23 15:26:42 +02:00
*p = '\0';
SSHORT dtype = var->sqltype & ~1; // may be modified by print_item_blob()
const SSHORT dscale = var->sqlscale;
2001-05-23 15:26:42 +02:00
// The printlength should have in it the right length
2001-05-23 15:26:42 +02:00
const SLONG length = printlength;
2001-05-23 15:26:42 +02:00
if (List) {
sprintf(Print_buffer, "%-31s ", fb_utils::exact_name(var->aliasname));
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
if ((var->sqltype & 1) && (*var->sqlind < 0)) {
/* If field was missing print <null> */
if (List) {
sprintf(Print_buffer, "<null>%s", NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
if ((dtype == SQL_TEXT) || (dtype == SQL_VARYING))
sprintf(p, "%-*s ", (int) length, "<null>");
else
sprintf(p, "%*s ", (int) length, "<null>");
}
else if (!strncmp(var->sqlname, "DB_KEY", 6)) {
2004-06-06 07:25:32 +02:00
// Special handling for db_keys printed in hex
2001-05-23 15:26:42 +02:00
// Keep a temp buf, d for building the binary string
2001-05-23 15:26:42 +02:00
for (const TEXT* t = var->sqldata; t < var->sqldata + var->sqllen; t++)
2004-06-06 07:25:32 +02:00
{
2001-05-23 15:26:42 +02:00
if (List) {
sprintf(Print_buffer, "%02X", (unsigned int) (UCHAR) *t);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
else {
sprintf(d, "%02X", (unsigned int) (UCHAR) *t);
2001-05-23 15:26:42 +02:00
strcat(p, d);
}
}
if (List)
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
else
strcat(p, " ");
}
else {
2004-06-06 07:25:32 +02:00
const ISC_QUAD* blobid;
TEXT blobbuf[30];
TEXT* string;
2001-05-23 15:26:42 +02:00
switch (dtype) {
case SQL_ARRAY:
// Print blob-ids only here
2001-05-23 15:26:42 +02:00
2004-06-06 07:25:32 +02:00
blobid = (ISC_QUAD*) var->sqldata;
sprintf(blobbuf, "%"xLONGFORMAT":%"xLONGFORMAT, blobid->gds_quad_high,
blobid->gds_quad_low);
2001-05-23 15:26:42 +02:00
sprintf(p, "%17s ", blobbuf);
break;
case SQL_BLOB:
// Print blob-ids only here
2001-05-23 15:26:42 +02:00
2004-06-06 07:25:32 +02:00
blobid = (ISC_QUAD*) var->sqldata;
sprintf(blobbuf, "%"xLONGFORMAT":%"xLONGFORMAT, blobid->gds_quad_high,
blobid->gds_quad_low);
2001-05-23 15:26:42 +02:00
sprintf(p, "%17s ", blobbuf);
if (List) {
ISQL_printf(isqlGlob.Out, blobbuf);
ISQL_printf(isqlGlob.Out, NEWLINE);
dtype = print_item_blob(isqlGlob.Out, var, M__trans);
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
}
break;
case SQL_SHORT:
case SQL_LONG:
case SQL_INT64:
{
TEXT str_buf[30];
SINT64 value;
if (dtype == SQL_SHORT)
value = (SINT64) (*(SSHORT*) (var->sqldata));
2001-05-23 15:26:42 +02:00
else if (dtype == SQL_LONG)
value = (SINT64) (*(SLONG*) (var->sqldata));
2001-05-23 15:26:42 +02:00
else if (dtype == SQL_INT64)
value = *(SINT64*) (var->sqldata);
2001-05-23 15:26:42 +02:00
print_item_numeric(value, length, dscale, str_buf);
sprintf(p, "%s ", str_buf);
if (List) {
// Added 1999-03-23 to left-justify in LIST ON mode
2001-05-23 15:26:42 +02:00
{
TEXT* src = str_buf;
TEXT* dest = str_buf;
2001-05-23 15:26:42 +02:00
while (' ' == *src)
src++;
/* This is pretty much the same thing as a strcpy,
but strcpy is not guaranteed to do the right thing
when the source and destination overlap. :-( */
if (src != dest)
while (0 != (*dest++ = *src++)); // empty
2001-05-23 15:26:42 +02:00
}
sprintf(Print_buffer, "%s%s", str_buf, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
break;
}
#ifdef NATIVE_QUAD
case SQL_QUAD:
if (dscale) {
// Handle floating scale and precision
2001-05-23 15:26:42 +02:00
double numeric = *(SQUAD*) (var->sqldata);
2004-06-06 07:25:32 +02:00
const double exponent = -dscale;
2001-05-23 15:26:42 +02:00
numeric = numeric / pow(10.0, exponent);
sprintf(p, "%*.*f ", (int) length, (int) -dscale, numeric);
if (List) {
sprintf(Print_buffer, "%.*f%s",
(int) -dscale, numeric, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
}
else {
sprintf(p, "%*ld ", (int) length, *(SQUAD*) (var->sqldata));
2001-05-23 15:26:42 +02:00
if (List) {
sprintf(Print_buffer, "%ld%s",
*(SQUAD*) var->sqldata, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
}
break;
#endif
case SQL_FLOAT:
//
// BRS 08 Aug 2003
// MSVC6 have a bug in the g format when used with # and display
// one digit more than the specified precision when the value is 0
// The bug appears in TCS DSQL_DOMAIN_12 and 13
//
#if (defined(_MSC_VER) && (_MSC_VER <= 1200)) || defined(MINGW)
if ((double) *(float*) (var->sqldata) == 0) {
sprintf(p, "% #*.*g ", (int) length,
(int) MIN(8, (length - 6)) - 1,
(double) *(float*) (var->sqldata));
if (List) {
sprintf(Print_buffer, "%.*g%s", FLOAT_LEN - 6 -1,
*(float*) (var->sqldata), NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
}
}
else {
sprintf(p, "% #*.*g ", (int) length,
(int) MIN(8, (length - 6)),
(double) *(float*) (var->sqldata));
if (List) {
sprintf(Print_buffer, "%.*g%s", FLOAT_LEN - 6,
*(float*) (var->sqldata), NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
}
}
#else
sprintf(p, "% #*.*g ", (int) length, (int) MIN(8, (length - 6)),
(double) *(float*) (var->sqldata));
2001-05-23 15:26:42 +02:00
if (List) {
sprintf(Print_buffer, "%.*g%s", FLOAT_LEN - 6,
*(float*) (var->sqldata), NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
#endif
2001-05-23 15:26:42 +02:00
break;
case SQL_DOUBLE:
// Don't let numeric/decimal doubles overflow print length
// Special handling for 0 -- don't test log for length
2001-05-23 15:26:42 +02:00
if ((!*var->sqldata && dscale)
|| (dscale
&& log10(fabs(*(double*) (var->sqldata))) <
length + dscale))
{
2001-05-23 15:26:42 +02:00
sprintf(p, "%*.*f ", (int) length, (int) -dscale,
*(double*) (var->sqldata));
2001-05-23 15:26:42 +02:00
if (List) {
sprintf(Print_buffer, "%.*f%s",
(int) -dscale,
*(double*) (var->sqldata), NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
}
else {
#if (defined(_MSC_VER) && (_MSC_VER <= 1200)) || defined(MINGW)
if (*(double*) (var->sqldata) == 0) {
sprintf(p, "% #*.*g ", (int) length,
(int) MIN(16, (int) (length - 7)) - 1,
*(double*) (var->sqldata));
if (List) {
sprintf(Print_buffer, "%#.*g%s",
DOUBLE_LEN - 7 - 1,
*(double*) (var->sqldata), NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
}
}
else {
sprintf(p, "% #*.*g ", (int) length,
(int) MIN(16, (int) (length - 7)),
*(double*) (var->sqldata));
if (List) {
sprintf(Print_buffer, "%#.*g%s",
DOUBLE_LEN - 7,
*(double*) (var->sqldata), NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
}
}
#else
2001-05-23 15:26:42 +02:00
sprintf(p, "% #*.*g ", (int) length,
(int) MIN(16, (int) (length - 7)),
*(double*) (var->sqldata));
2001-05-23 15:26:42 +02:00
if (List) {
sprintf(Print_buffer, "%#.*g%s",
DOUBLE_LEN - 7,
*(double*) (var->sqldata), NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
#endif
2001-05-23 15:26:42 +02:00
}
break;
case SQL_TEXT:
string = var->sqldata;
string[var->sqllen] = '\0';
// See if it is character set OCTETS
2001-05-23 15:26:42 +02:00
if (var->sqlsubtype == 1) {
TEXT* buff2 = (TEXT*) ISQL_ALLOC(2 * var->sqllen + 1);
// Convert the string to hex digits
for (USHORT i = 0; i < var->sqllen; i++) {
sprintf(&buff2[i * 2], "%02X", (UCHAR)string[i]);
2001-05-23 15:26:42 +02:00
}
buff2[(2 * var->sqllen + 1) - 1] = 0;
if (List) {
ISQL_printf(isqlGlob.Out, buff2);
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
}
else
sprintf(p, "%s ", buff2);
ISQL_FREE(buff2);
}
else if (List) {
ISQL_printf(isqlGlob.Out, string);
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
}
else {
// Truncate if necessary
2001-05-23 15:26:42 +02:00
if (length < var->sqllen)
string[length] = '\0';
sprintf(p, "%-*s ", (int) length, var->sqldata);
}
break;
case SQL_TIMESTAMP:
isc_decode_date((ISC_QUAD*)var->sqldata, &times);
2001-05-23 15:26:42 +02:00
if (isqlGlob.SQL_dialect > SQL_DIALECT_V5) {
2003-04-08 12:38:59 +02:00
sprintf(d, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT,
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
}
else {
if (Time_display)
2003-04-08 12:38:59 +02:00
sprintf(d, "%2d-%s-%4d %2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT,
2001-05-23 15:26:42 +02:00
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);
2001-05-23 15:26:42 +02:00
else
sprintf(d, "%2d-%s-%4d", times.tm_mday,
alpha_months[times.tm_mon], times.tm_year + 1900);
}
sprintf(p, "%-*s ", (int) length, d);
if (List) {
sprintf(Print_buffer, "%s%s", d, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
break;
case SQL_TYPE_TIME:
{
/* Temporarily convert the Time to a Time & Date in order
to use isc_decode_date () */
ULONG time_and_date[2];
time_and_date[0] = 0;
time_and_date[1] = *(ULONG*) var->sqldata;
isc_decode_date((ISC_QUAD*)time_and_date, &times);
2003-04-08 12:38:59 +02:00
sprintf(d, "%2.2d:%2.2d:%2.2d.%4.4"ULONGFORMAT,
2001-05-23 15:26:42 +02:00
times.tm_hour, times.tm_min, times.tm_sec,
(*(ULONG*) var->sqldata) % ISC_TIME_SECONDS_PRECISION);
2001-05-23 15:26:42 +02:00
sprintf(p, "%-*s ", (int) length, d);
if (List) {
sprintf(Print_buffer, "%s%s", d, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
break;
2001-05-23 15:26:42 +02:00
}
case SQL_TYPE_DATE:
{
/* Temporarily convert the Time to a Time & Date in order
to use isc_decode_date () */
ULONG time_and_date[2];
time_and_date[0] = *(ULONG*) var->sqldata;
2001-05-23 15:26:42 +02:00
time_and_date[1] = 0;
isc_decode_date((ISC_QUAD*)time_and_date, &times);
2001-05-23 15:26:42 +02:00
sprintf(d, "%4.4d-%2.2d-%2.2d", times.tm_year + 1900,
(times.tm_mon + 1), times.tm_mday);
sprintf(p, "%-*s ", (int) length, d);
if (List) {
sprintf(Print_buffer, "%s%s", d, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
}
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;
2001-05-23 15:26:42 +02:00
// If CHARACTER SET OCTETS, print two hex digits per octet
if (var->sqlsubtype == 1)
{
2004-06-06 07:25:32 +02:00
const SLONG hex_len = 2 * avary->vary_length;
char* buff2 = static_cast<char*>(ISQL_ALLOC(hex_len + 1));
char* bp = buff2;
2001-05-23 15:26:42 +02:00
for (USHORT i = 0; i < avary->vary_length; i++, bp += 2)
{
sprintf(bp, "%02X",
static_cast<UCHAR>(avary->vary_string[i]));
}
buff2[hex_len] = '\0'; // there is an extra byte for terminator
if (List)
{
ISQL_printf(isqlGlob.Out, buff2);
ISQL_printf(isqlGlob.Out, NEWLINE);
}
else
{
// Truncate if necessary
sprintf(p, "%-*.*s ", static_cast<int>(length),
static_cast<int>(MIN(length, hex_len)), buff2);
}
ISQL_FREE(buff2);
}
else if (List) {
ISQL_printf(isqlGlob.Out, avary->vary_string);
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
}
else {
// Truncate if necessary
if (length < avary->vary_length)
avary->vary_length = length;
sprintf(p, "%-*.*s ", (int) length, (int) avary->vary_length,
avary->vary_string);
2001-05-23 15:26:42 +02:00
}
break;
}
2001-05-23 15:26:42 +02:00
default:
sprintf(d, "Unknown type: %d", dtype);
sprintf(p, "%-*s ", (int) length, d);
if (List) {
sprintf(Print_buffer, "%s%s", d, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
break;
}
}
while (*p)
++p;
*s = p;
return (dtype);
}
2004-04-29 00:36:29 +02:00
static processing_state print_item_blob(FILE* fp,
const XSQLVAR* var,
2004-05-03 01:06:37 +02:00
FB_API_HANDLE trans)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r i n t _ i t e m _ b l o b
*
**************************************
*
* Functional description
* Print a User Selected BLOB field (as oppposed to
2001-05-23 15:26:42 +02:00
* any BLOB selected in a show or extract command)
*
**************************************/
TEXT* buffer = (TEXT*) ISQL_ALLOC((SLONG) BUFFER_LENGTH512);
2001-05-23 15:26:42 +02:00
if (!buffer)
return ERR;
TEXT* msg = (TEXT*) ISQL_ALLOC((SLONG) MSG_LENGTH);
2001-05-23 15:26:42 +02:00
if (!msg) {
ISQL_FREE(buffer);
return ERR;
}
ISC_QUAD* blobid = (ISC_QUAD*) var->sqldata;
2001-05-23 15:26:42 +02:00
// Don't bother with null blobs
2001-05-23 15:26:42 +02:00
if (blobid->gds_quad_high == 0 && blobid->gds_quad_low == 0)
return CONT;
if ((var->sqlsubtype != Doblob) && (Doblob != ALL_BLOBS)) {
ISQL_msg_get(BLOB_SUBTYPE, msg, (TEXT*) (IPTR) Doblob,
(TEXT*) (IPTR) var->sqlsubtype, NULL, NULL, NULL);
2004-03-07 08:58:55 +01:00
// Blob display set to subtype %d. This blob: subtype = %d\n
2001-05-23 15:26:42 +02:00
ISQL_printf(fp, msg);
if (buffer)
ISQL_FREE(buffer);
if (msg)
ISQL_FREE(msg);
return CONT;
}
USHORT bpb_length = 0;
const UCHAR* bpb = NULL;
UCHAR bpb_buffer[64];
ISC_BLOB_DESC from_desc;
const int blob_subtype = var->sqlsubtype;
if (blob_subtype == isc_blob_text) {
2001-05-23 15:26:42 +02:00
/* Lookup the remaining descriptor information for the BLOB field,
* most specifically we're interested in the Character Set so
* we can set up a BPB that requests character set transliteration
*/
// CVC: Adriano changed trans to D__trans with the following comment:
// Fix a bug that occur when a BLOB column is added after the start
// of the DML transaction.
if (!isc_blob_lookup_desc(isc_status, &DB, &D__trans,
(const UCHAR*) var->relname, (const UCHAR*) var->sqlname,
&from_desc, NULL))
{
ISC_BLOB_DESC to_desc;
isc_blob_default_desc(&to_desc, (const UCHAR*) var->relname, (const UCHAR*) var->sqlname);
2001-05-23 15:26:42 +02:00
if (!isc_blob_gen_bpb
(isc_status, &to_desc, &from_desc, sizeof(bpb_buffer),
bpb_buffer, &bpb_length))
{
bpb = bpb_buffer;
}
2001-05-23 15:26:42 +02:00
}
}
else if (blob_subtype > isc_blob_text && blob_subtype < isc_blob_max_predefined_subtype)
{
bpb = predefined_blob_subtype_bpb;
bpb_length = sizeof(predefined_blob_subtype_bpb);
set_bpb_for_translation(blob_subtype);
2001-05-23 15:26:42 +02:00
}
2004-05-03 01:06:37 +02:00
FB_API_HANDLE blob = 0;
if (isc_open_blob2(isc_status, &DB, &trans, &blob, blobid, bpb_length,
bpb))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
if (buffer)
ISQL_FREE(buffer);
if (msg)
ISQL_FREE(msg);
return ERR;
}
USHORT length;
while (!isc_get_segment(isc_status, &blob, &length,
(USHORT) (BUFFER_LENGTH512 - 1), buffer)
|| isc_status[1] == isc_segment)
{
// Special displays for blr or acl subtypes
2001-05-23 15:26:42 +02:00
if (blob_subtype > isc_blob_text && blob_subtype < isc_blob_max_predefined_subtype)
{
2001-05-23 15:26:42 +02:00
buffer[length--] = 0;
for (char* b = buffer + length; b >= buffer;) {
2001-05-23 15:26:42 +02:00
if (*b == '\n' || *b == '\t' || *b == BLANK)
*b-- = 0;
else
break;
}
sprintf(Print_buffer, "%s %s%s", TAB_AS_SPACES, buffer, NEWLINE);
ISQL_printf(fp, Print_buffer);
2001-05-23 15:26:42 +02:00
}
else {
buffer[length] = 0;
ISQL_printf(fp, buffer);
2001-05-23 15:26:42 +02:00
}
}
if (isc_status[1] == isc_segstr_eof)
isc_close_blob(isc_status, &blob);
2001-05-23 15:26:42 +02:00
else if (isc_status[1]) {
ISQL_errmsg(isc_status);
if (buffer)
ISQL_FREE(buffer);
if (msg)
ISQL_FREE(msg);
return ERR;
}
if (buffer)
ISQL_FREE(buffer);
if (msg)
ISQL_FREE(msg);
return CONT;
}
static void print_item_numeric(SINT64 value,
SSHORT length,
SSHORT scale,
TEXT* buf)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r i n t _ i t e m _ n u m e r i c
*
**************************************
*
* Functional description
* Print a INT64 value into a buffer by accomodating
* decimal point '.' for scale notation
*
**************************************/
bool all_digits_used = false;
2003-09-08 03:45:09 +02:00
bool neg = (value < 0);
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
// Handle special case of no negative scale, no '.' required!
2001-05-23 15:26:42 +02:00
if (scale >= 0) {
if (scale > 0)
value *= (SINT64) pow(10.0, (double) scale);
sprintf(buf, "%*" QUADFORMAT "d", length, value);
return;
}
// Use one less space than allowed, to leave room for '.'
2001-05-23 15:26:42 +02:00
length--;
sprintf(buf, "%*" QUADFORMAT "d", length, value);
// start from LSByte towards MSByte
SSHORT from = length - 1;
SSHORT to = length;
2001-05-23 15:26:42 +02:00
// make space for decimal '.' point in buffer
2001-05-23 15:26:42 +02:00
buf[to + 1] = '\0';
for (; from >= 0 && DIGIT(buf[from]) && scale; from--, to--, ++scale)
buf[to] = buf[from];
2004-03-07 08:58:55 +01:00
// Check whether we need a '0' (zero) in front of the '.' point
2001-05-23 15:26:42 +02:00
if (!DIGIT(buf[from]))
all_digits_used = true;
2001-05-23 15:26:42 +02:00
/* Insert new '0's to satisfy larger scale than input digits
2001-05-23 15:26:42 +02:00
* For e.g: 12345 with scale -7 would be .0012345
*/
if (from > 0 && scale)
do {
buf[to--] = '0';
} while (++scale != 0);
// Insert '.' decimal point, and if required, '-' and '0'
2001-05-23 15:26:42 +02:00
buf[to--] = '.';
2003-09-09 13:03:37 +02:00
if (all_digits_used) {
2001-05-23 15:26:42 +02:00
buf[to--] = '0';
2003-09-09 13:03:37 +02:00
if (neg)
2001-05-23 15:26:42 +02:00
buf[to--] = '-';
}
}
static processing_state print_line(XSQLDA* sqlda,
const SLONG* pad,
TEXT* line)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r i n t _ l i n e
*
**************************************
*
* Functional description
* Print a line of SQL variables.
*
* Args: sqlda, an XSQLDA
* pad = an array of the print lengths of all the columns
* line = pointer to the line buffer.
2001-05-23 15:26:42 +02:00
*
**************************************/
XSQLVAR* varlist[20]; // No more than 20 blobs per line
int varnum = -1;
{ // scope
TEXT* p = line;
int i = 0;
XSQLVAR* var = sqlda->sqlvar;
for (const XSQLVAR* const end = var + sqlda->sqld; var < end;
var++, i++)
{
if (!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]);
if (rc == SQL_BLOB) {
varnum++;
varlist[varnum] = var;
}
2003-12-03 09:19:24 +01:00
}
}
*p = 0;
} // scope
2001-05-23 15:26:42 +02:00
if (List) {
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
return (CONT);
}
ISQL_printf(isqlGlob.Out, line);
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
// If blobdisplay is not wanted, set varnum back to -1
2001-05-23 15:26:42 +02:00
if (Doblob == NO_BLOBS)
varnum = -1;
// If there were Blobs to print, print them passing the blobid
2001-05-23 15:26:42 +02:00
for (int i = 0; i <= varnum; i++) {
const XSQLVAR* var = varlist[i];
2001-05-23 15:26:42 +02:00
if (*var->sqlind == 0) {
// Print blob title
2001-05-23 15:26:42 +02:00
sprintf(Print_buffer,
"==============================================================================%s",
NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
sprintf(Print_buffer, "%s: %s", var->aliasname, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
if (print_item_blob(isqlGlob.Out, var, M__trans))
2001-05-23 15:26:42 +02:00
return (ERR);
sprintf(Print_buffer,
"%s==============================================================================%s",
NEWLINE, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
}
return (CONT);
}
static int process_statement(const TEXT* string,
XSQLDA** sqldap)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r o c e s s _ s t a t e m e n t
*
**************************************
*
* Functional description
* Prepare and execute a dynamic SQL statement.
* 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.
* This function now returns CONT (success) or ERR
2001-05-23 15:26:42 +02:00
**************************************/
processing_state ret = CONT;
static const SCHAR sqlda_info[] = { isc_info_sql_stmt_type };
static const SCHAR plan_info[] = { isc_info_sql_get_plan };
static const SCHAR count_info[] = { isc_info_sql_records };
// 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
2001-05-23 15:26:42 +02:00
if (!M__trans)
if (isc_start_transaction(isc_status, &M__trans, 1, &DB, 0, NULL))
ISQL_errmsg(isc_status);
2001-05-23 15:26:42 +02:00
// No need to start a default transaction unless there is no current one
2001-05-23 15:26:42 +02:00
if (!D__trans)
if (isc_start_transaction(isc_status, &D__trans, 1, &DB, 5, default_tpb))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
}
2001-05-23 15:26:42 +02:00
// If statistics are requested, then reserve them here
2001-05-23 15:26:42 +02:00
if (Stats)
perf_get_info(&DB, &perf_before);
2001-05-23 15:26:42 +02:00
/* Prepare the dynamic query stored in string.
But put this on the DDL transaction to get maximum visibility of
metadata. */
2001-05-23 15:26:42 +02:00
2004-05-03 01:06:37 +02:00
FB_API_HANDLE prepare_trans;
2001-05-23 15:26:42 +02:00
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))
{
if (isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION && Input_file) {
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, "**** Error preparing statement:");
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, string);
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
}
ISQL_errmsg(isc_status);
return ERR;
}
// check for warnings
2001-05-23 15:26:42 +02:00
if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
// Find out what kind of statement this is
SSHORT statement_type = 0;
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 (info_buffer[0] == isc_info_sql_stmt_type) {
const UCHAR* b = reinterpret_cast<const UCHAR*>(info_buffer);
const SSHORT l = gds__vax_integer(b + 1, 2);
statement_type = gds__vax_integer(b + 3, l);
}
}
2002-06-29 15:39:11 +02:00
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
if (Sqlda_display) {
2004-08-27 07:00:31 +02:00
// Describe the input
XSQLDA* input_sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(10)));
2001-05-23 15:26:42 +02:00
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)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return ERR;
}
else if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
// Reallocate if there's LOTS of input
2001-05-23 15:26:42 +02:00
if (input_sqlda->sqld > input_sqlda->sqln) {
USHORT n_columns = input_sqlda->sqld;
ISQL_FREE(input_sqlda);
input_sqlda =
(XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(n_columns)));
2001-05-23 15:26:42 +02:00
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))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return ERR;
}
else if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
}
if ((statement_type == isc_info_sql_stmt_select) ||
(statement_type == isc_info_sql_stmt_select_for_upd) ||
(statement_type == isc_info_sql_stmt_exec_procedure))
{
TEXT buffer[100];
2003-04-08 12:38:59 +02:00
sprintf(buffer,
2001-05-23 15:26:42 +02:00
"\nINPUT SQLDA version: %d sqldaid: %s sqldabc: %ld sqln: %d sqld: %d\n",
input_sqlda->version, input_sqlda->sqldaid,
input_sqlda->sqldabc, input_sqlda->sqln,
input_sqlda->sqld);
ISQL_printf(isqlGlob.Out, buffer);
int i = 0;
const XSQLVAR* var = input_sqlda->sqlvar;
for (const XSQLVAR* const end = var + input_sqlda->sqld; var < end;
var++, i++)
{
2003-04-08 12:38:59 +02:00
sprintf(buffer,
2001-05-23 15:26:42 +02:00
"%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" : " ",
2001-05-23 15:26:42 +02:00
var->sqlscale, var->sqlsubtype, var->sqllen);
ISQL_printf(isqlGlob.Out, buffer);
2003-04-08 12:38:59 +02:00
sprintf(buffer, " : name: (%d)%*s alias: (%d)%*s\n",
2001-05-23 15:26:42 +02:00
var->sqlname_length, var->sqlname_length,
var->sqlname, var->aliasname_length,
var->aliasname_length, var->aliasname);
ISQL_printf(isqlGlob.Out, buffer);
2003-04-08 12:38:59 +02:00
sprintf(buffer, " : table: (%d)%*s owner: (%d)%*s\n",
2001-05-23 15:26:42 +02:00
var->relname_length, var->relname_length,
var->relname, var->ownname_length,
var->ownname_length, var->ownname);
ISQL_printf(isqlGlob.Out, buffer);
2001-05-23 15:26:42 +02:00
}
}
ISQL_FREE(input_sqlda);
}
#endif
// check for warnings
2001-05-23 15:26:42 +02:00
if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
// if PLAN is set, print out the plan now
2002-06-29 15:39:11 +02:00
2001-05-23 15:26:42 +02:00
if (Plan) {
// Bug 7565: A plan larger than plan_buffer will not be displayed
// Bug 7565: Note also that the plan must fit into Print_Buffer
2001-05-23 15:26:42 +02:00
TEXT* plan_buffer = (SCHAR*) ISQL_ALLOC(PRINT_BUFFER_LENGTH);
2001-05-23 15:26:42 +02:00
memset(plan_buffer, 0, PRINT_BUFFER_LENGTH);
if (isc_dsql_sql_info(isc_status, &global_Stmt, sizeof(plan_info), plan_info,
PRINT_BUFFER_LENGTH, plan_buffer))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
}
else if (plan_buffer[0] == isc_info_sql_get_plan) {
const SSHORT l =
gds__vax_integer(reinterpret_cast<const UCHAR*>(plan_buffer) + 1, 2);
2001-05-23 15:26:42 +02:00
sprintf(Print_buffer, "%.*s%s", l, plan_buffer + 3, NEWLINE);
ISQL_printf(Diag, Print_buffer);
}
ISQL_FREE(plan_buffer);
if (Planonly)
{
return ret; // do not execute
2001-05-23 15:26:42 +02:00
}
}
// If the statement isn't a select, execute it and be done
2001-05-23 15:26:42 +02:00
if (statement_type != isc_info_sql_stmt_select
&& statement_type != isc_info_sql_stmt_select_for_upd
&& statement_type != isc_info_sql_stmt_exec_procedure)
{
2002-06-29 15:39:11 +02:00
// If this is an autocommit, put the DDL stmt on a special trans
2001-05-23 15:26:42 +02:00
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))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
ret = ERR;
}
commit_trans(&D__trans);
2001-05-23 15:26:42 +02:00
// check for warnings
2001-05-23 15:26:42 +02:00
if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
return ret;
}
// Check to see if this is a SET TRANSACTION statement
2001-05-23 15:26:42 +02:00
if (statement_type == isc_info_sql_stmt_start_trans) {
newtrans(string);
return ret;
}
// This is a non-select DML statement or trans
2001-05-23 15:26:42 +02:00
if (isc_dsql_execute(isc_status, &M__trans, &global_Stmt, isqlGlob.SQL_dialect, NULL))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
// CVC: Make this conditional if it causes problems. For example
// if (BailOnError)
ret = ERR;
}
2001-05-23 15:26:42 +02:00
// check for warnings
2001-05-23 15:26:42 +02:00
if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
// We are executing a commit or rollback, commit default trans
2001-05-23 15:26:42 +02:00
if ((statement_type == isc_info_sql_stmt_commit) ||
(statement_type == isc_info_sql_stmt_rollback))
{
commit_trans(&D__trans);
}
2001-05-23 15:26:42 +02:00
// Print statistics report
2001-05-23 15:26:42 +02:00
if (Docount) {
UCHAR count_type = 0;
SLONG count = 0;
2001-05-23 15:26:42 +02:00
if (statement_type == isc_info_sql_stmt_update)
count_type = isc_info_req_update_count;
if (statement_type == isc_info_sql_stmt_delete)
count_type = isc_info_req_delete_count;
// Skip selects, better to count records incoming later
2001-05-23 15:26:42 +02:00
if (statement_type == isc_info_sql_stmt_insert)
count_type = isc_info_req_insert_count;
if (count_type) {
SCHAR count_buffer[33];
isc_dsql_sql_info(isc_status, &global_Stmt, sizeof(count_info),
2001-05-23 15:26:42 +02:00
count_info, sizeof(count_buffer),
count_buffer);
const UCHAR* p = reinterpret_cast<const UCHAR*>(count_buffer + 3);
while (*p != isc_info_end) {
const UCHAR count_is = *p++;
const SSHORT l = gds__vax_integer(p, 2);
2001-05-23 15:26:42 +02:00
p += 2;
count = gds__vax_integer(p, l);
2001-05-23 15:26:42 +02:00
p += l;
if (count_is == count_type)
break;
}
ISQL_msg_get(REC_COUNT, rec_count_msg, (TEXT*)(IPTR) count, NULL, NULL,
2001-05-23 15:26:42 +02:00
NULL, NULL);
// Records affected: %ld
sprintf(Print_buffer, "%s%s", rec_count_msg, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
}
if (Stats) {
perf_get_info(&DB, &perf_after);
2001-05-23 15:26:42 +02:00
if (!have_report) {
ISQL_msg_get(REPORT1, report_1, NULL, NULL, NULL, NULL, NULL);
2004-03-07 08:58:55 +01:00
// Current memory = !c\nDelta memory = !d\nMax memory = !x\nElapsed time= !e sec\n
2001-05-23 15:26:42 +02:00
ISQL_msg_get(REPORT2, &report_1[strlen(report_1)], NULL, NULL,
NULL, NULL, NULL);
/* 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;
2001-05-23 15:26:42 +02:00
}
perf_format(&perf_before, &perf_after, report_1, statistics, NULL);
2001-05-23 15:26:42 +02:00
sprintf(Print_buffer, "%s%s", statistics, NEWLINE);
ISQL_printf(Diag, Print_buffer);
}
return (ret);
}
const SSHORT n_cols = sqlda->sqld;
2001-05-23 15:26:42 +02:00
// The query is bigger than the curent sqlda, so make more room
2001-05-23 15:26:42 +02:00
if (n_cols > sqlda->sqln) {
ISQL_FREE(sqlda);
sqlda = (XSQLDA*) ISQL_ALLOC((SLONG) (XSQLDA_LENGTH(n_cols)));
2001-05-23 15:26:42 +02:00
*sqldap = sqlda;
sqlda->version = SQLDA_VERSION1;
sqlda->sqld = sqlda->sqln = n_cols;
// We must re-describe the sqlda, no need to re-prepare
2001-05-23 15:26:42 +02:00
isc_dsql_describe(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda);
2001-05-23 15:26:42 +02:00
}
if (statement_type != isc_info_sql_stmt_exec_procedure) {
// Otherwise, open the cursor to start things up
2001-05-23 15:26:42 +02:00
// Declare the C cursor for what it is worth
2001-05-23 15:26:42 +02:00
if (isc_dsql_set_cursor_name(isc_status, &global_Stmt, "C ", 0)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return (ERR);
}
// check for warnings
2001-05-23 15:26:42 +02:00
if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
if (isc_dsql_execute(isc_status, &M__trans, &global_Stmt, isqlGlob.SQL_dialect, NULL)) {
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
return (ERR);
}
// check for warnings
2001-05-23 15:26:42 +02:00
if (isc_status[2] == isc_arg_warning)
ISQL_warning(isc_status);
}
#ifdef DEV_BUILD
/* To facilitate debugging, the option
2001-05-23 15:26:42 +02:00
SET SQLDA_DISPLAY ON
will activate code to display the SQLDA after each statement. */
if (Sqlda_display) {
TEXT buffer[100];
2003-04-08 12:38:59 +02:00
sprintf(buffer,
2001-05-23 15:26:42 +02:00
"\nOUTPUT SQLDA version: %d sqldaid: %s sqldabc: %ld sqln: %d sqld: %d\n",
sqlda->version, sqlda->sqldaid, sqlda->sqldabc, sqlda->sqln,
sqlda->sqld);
ISQL_printf(isqlGlob.Out, buffer);
int i = 0;
const XSQLVAR* var = sqlda->sqlvar;
for (const XSQLVAR* const end = var + sqlda->sqld; var < end;
var++, i++)
{
2003-04-08 12:38:59 +02:00
sprintf(buffer,
2001-05-23 15:26:42 +02:00
"%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" : " ",
2001-05-23 15:26:42 +02:00
var->sqlscale, var->sqlsubtype, var->sqllen);
ISQL_printf(isqlGlob.Out, buffer);
2003-04-08 12:38:59 +02:00
sprintf(buffer, " : name: (%d)%*s alias: (%d)%*s\n",
2001-05-23 15:26:42 +02:00
var->sqlname_length, var->sqlname_length, var->sqlname,
var->aliasname_length, var->aliasname_length,
var->aliasname);
ISQL_printf(isqlGlob.Out, buffer);
2003-04-08 12:38:59 +02:00
sprintf(buffer, " : table: (%d)%*s owner: (%d)%*s\n",
2001-05-23 15:26:42 +02:00
var->relname_length, var->relname_length, var->relname,
var->ownname_length, var->ownname_length, var->ownname);
ISQL_printf(isqlGlob.Out, buffer);
2001-05-23 15:26:42 +02:00
}
}
#endif
SSHORT* nullind = NULL;
if (n_cols) {
nullind = (SSHORT*) ISQL_ALLOC((SLONG) (n_cols * sizeof(SSHORT)));
}
2002-06-29 15:39:11 +02:00
2001-05-23 15:26:42 +02:00
#ifdef DEBUG_GDS_ALLOC
if (n_cols && nullind) {
gds_alloc_flag_unfreed ((void*) nullind);
}
2001-05-23 15:26:42 +02:00
#endif
2002-06-29 15:39:11 +02:00
SLONG* buffer = NULL;
{ // scope
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;
}
2001-05-23 15:26:42 +02:00
bufsize = FB_ALIGN(bufsize, alignment) + data_length;
var->sqlind = nullp++;
}
// Buffer for reading data from the fetch
if (bufsize) {
buffer = (SLONG*) ISQL_ALLOC ((SLONG) bufsize);
memset(buffer, 0, (size_t) bufsize);
}
} // scope
// Pad is an array of lengths to be passed to the print_item
SLONG* pad = NULL;
if (sqlda->sqld) {
pad = (SLONG*) ISQL_ALLOC ((SLONG) (sqlda->sqld * sizeof (SLONG)));
}
2001-05-23 15:26:42 +02:00
SLONG linelength = 0;
{ // scope
SLONG offset = 0;
int i = 0;
XSQLVAR* var = sqlda->sqlvar;
for (const XSQLVAR* const end = var + sqlda->sqld; var < end;
var++, i++)
{
/* record the length of name and var, then convert to print
** length , later to be stored in pad array
*/
2001-05-23 15:26:42 +02:00
USHORT data_length, disp_length, alignment;
data_length = disp_length = alignment = var->sqllen;
SSHORT namelength = var->aliasname_length;
2001-05-23 15:26:42 +02:00
/* minimum display length should not be less than that needed
** for displaying null
*/
2001-05-23 15:26:42 +02:00
if (namelength < NULL_DISP_LEN)
namelength = NULL_DISP_LEN;
2001-05-23 15:26:42 +02:00
const SSHORT type = var->sqltype & ~1;
2001-05-23 15:26:42 +02:00
switch (type) {
case SQL_BLOB:
case SQL_ARRAY:
// enough room for the blob id to print
2001-05-23 15:26:42 +02:00
disp_length = 17;
break;
case SQL_TIMESTAMP:
if (Time_display || isqlGlob.SQL_dialect > SQL_DIALECT_V5)
disp_length = DATETIME_LEN;
else
disp_length = DATE_ONLY_LEN;
break;
case SQL_TYPE_TIME:
disp_length = TIME_ONLY_LEN;
break;
case SQL_TYPE_DATE:
disp_length = DATE_ONLY_LEN;
break;
case SQL_FLOAT:
disp_length = FLOAT_LEN;
break;
case SQL_DOUBLE:
disp_length = DOUBLE_LEN;
break;
case SQL_TEXT:
alignment = 1;
data_length++;
// OCTETS data is displayed in hex
if (var->sqlsubtype == 1)
disp_length = 2 * var->sqllen;
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;
break;
case SQL_SHORT:
disp_length = SHORT_LEN;
break;
case SQL_LONG:
disp_length = LONG_LEN;
break;
case SQL_INT64:
disp_length = INT64_LEN;
break;
#ifdef NATIVE_QUAD
case SQL_QUAD:
disp_length = QUAD_LEN;
break;
#endif
default:
disp_length = UNKNOWN_LEN;
break;
}
2001-05-23 15:26:42 +02:00
// special case db_key alignment which arrives as an SQL_TEXT
2001-05-23 15:26:42 +02:00
if (!strncmp(var->sqlname, "DB_KEY", 6)) {
alignment = 8;
disp_length = 2 * var->sqllen;
}
2001-05-23 15:26:42 +02:00
// This is the print width of each column
2001-05-23 15:26:42 +02:00
pad[i] = (disp_length > namelength ? disp_length : namelength);
// Is there a collist entry, then use that width, but only for text
if (type == SQL_TEXT || type == SQL_VARYING)
{
collist* pc = global_Cols;
while (pc) {
if (!strcmp(var->aliasname, pc->col_name)) {
pad[i] = pc->col_len;
break;
}
pc = pc->collist_next;
2001-05-23 15:26:42 +02:00
}
}
// The total line length
2001-05-23 15:26:42 +02:00
linelength += pad[i] + 1;
2001-05-23 15:26:42 +02:00
// Allocate space in buffer for data
2001-05-23 15:26:42 +02:00
offset = FB_ALIGN(offset, alignment);
var->sqldata = (SCHAR*) buffer + offset;
offset += data_length;
}
} // scope
2001-05-23 15:26:42 +02:00
// Add a few for line termination, et al
2001-05-23 15:26:42 +02:00
linelength += 10;
// Allocate the print line and the header line and the separator
2001-05-23 15:26:42 +02:00
TEXT* line = (TEXT*) ISQL_ALLOC((SLONG) linelength);
TEXT* header = 0;
TEXT* header2 = 0;
if (Heading)
{
header = (TEXT*) ISQL_ALLOC((SLONG) linelength);
header2 = (TEXT*) ISQL_ALLOC((SLONG) linelength);
*header = '\0';
*header2 = '\0';
// Create the column header : Left justify strings
2001-05-23 15:26:42 +02:00
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++)
{
const SSHORT type = var->sqltype & ~1;
if (type == SQL_TEXT || type == SQL_VARYING)
sprintf(p, "%-*.*s ", (int) (pad[i]), (int) (pad[i]),
var->aliasname);
else
sprintf(p, "%*s ", (int) (pad[i]), var->aliasname);
// Separators need not go on forever no more than a line
for (size_t j = 1; j < strlen(p) && j < 80; j++)
*p2++ = '=';
*p2++ = BLANK;
p += strlen(p);
}
*p2 = '\0';
2001-05-23 15:26:42 +02:00
}
// If this is an exec procedure, execute into the sqlda with one fetch only
2001-05-23 15:26:42 +02:00
if (statement_type == isc_info_sql_stmt_exec_procedure) {
if (isc_dsql_execute2(isc_status, &M__trans, &global_Stmt,
isqlGlob.SQL_dialect, NULL, sqlda))
{
2001-05-23 15:26:42 +02:00
ISQL_errmsg(isc_status);
ret = ERR;
}
else {
if (sqlda->sqld) { // do not output unnecessary white text
ISQL_printf(isqlGlob.Out, NEWLINE);
if (!List && Heading) {
ISQL_printf(isqlGlob.Out, header);
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, header2);
ISQL_printf(isqlGlob.Out, NEWLINE);
}
print_line(sqlda, pad, line);
ISQL_printf(isqlGlob.Out, NEWLINE);
}
}
}
2001-05-23 15:26:42 +02:00
// Otherwise fetch and print records until EOF
2001-05-23 15:26:42 +02:00
else {
SLONG lines;
for (lines = 0; !Abort_flag; ++lines) {
2001-05-23 15:26:42 +02:00
// Fetch the current cursor
2001-05-23 15:26:42 +02:00
#ifdef SCROLLABLE_CURSORS
if (Autofetch) {
#endif
if (isc_dsql_fetch(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda) == 100)
break;
2001-05-23 15:26:42 +02:00
#ifdef SCROLLABLE_CURSORS
}
else {
TEXT fetch_statement[BUFFER_LENGTH80];
2001-05-23 15:26:42 +02:00
/* if not autofetching, sit and wait for user to input
2001-05-23 15:26:42 +02:00
NEXT, PRIOR, FIRST, LAST, ABSOLUTE <n>, or RELATIVE <n> */
const SSHORT fetch_ret =
get_statement(fetch_statement, sizeof(fetch_statement),
2001-05-23 15:26:42 +02:00
fetch_prompt);
if (fetch_ret == FETCH) {
if (isc_dsql_fetch2(isc_status, &global_Stmt, isqlGlob.SQL_dialect, sqlda,
fetch_direction, fetch_offset) == 100)
{
2001-05-23 15:26:42 +02:00
break;
}
}
else if (fetch_ret == BACKOUT || fetch_ret == EXIT) {
lines = 0;
break;
}
else {
TEXT errbuf[MSG_LENGTH];
gds__msg_format(NULL, ISQL_MSG_FAC, CMD_ERR, MSG_LENGTH,
errbuf,
2001-05-23 15:26:42 +02:00
"Expected NEXT, PRIOR, FIRST, LAST, ABSOLUTE <n>, RELATIVE <n>, or QUIT",
NULL, NULL, NULL, NULL);
2001-05-23 15:26:42 +02:00
STDERROUT(errbuf, 1);
}
}
#endif
/* Print the header every Pagelength number of lines for
command-line ISQL only. For WISQL, print the column
headings only once. */
if (!List && Heading && (lines % Pagelength == 0)) {
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, header);
ISQL_printf(isqlGlob.Out, NEWLINE);
ISQL_printf(isqlGlob.Out, header2);
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
}
if (isc_status[1]) {
ISQL_errmsg(isc_status);
isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_close);
2001-05-23 15:26:42 +02:00
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 (ERR);
}
if (!lines)
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
ret = print_line(sqlda, pad, line);
}
if (lines)
ISQL_printf(isqlGlob.Out, NEWLINE);
2001-05-23 15:26:42 +02:00
// Record count printed here upon request
2001-05-23 15:26:42 +02:00
if (Docount) {
ISQL_msg_get(REC_COUNT, rec_count_msg, (TEXT*)(IPTR) lines, NULL, NULL,
2001-05-23 15:26:42 +02:00
NULL, NULL);
// Total returned: %ld
sprintf(Print_buffer, "%s%s", rec_count_msg, NEWLINE);
2001-05-23 15:26:42 +02:00
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
}
if (statement_type != isc_info_sql_stmt_exec_procedure) {
isc_dsql_free_statement(isc_status, &global_Stmt, DSQL_close);
}
2001-05-23 15:26:42 +02:00
// Statistics printed here upon request
2001-05-23 15:26:42 +02:00
if (Stats) {
perf_get_info(&DB, &perf_after);
2001-05-23 15:26:42 +02:00
if (!have_report) {
ISQL_msg_get(REPORT1, report_1, NULL, NULL, NULL, NULL, NULL);
2004-03-07 08:58:55 +01:00
// Current memory = !c\nDelta memory = !d\nMax memory = !x\nElapsed time= !e sec\n
2001-05-23 15:26:42 +02:00
ISQL_msg_get(REPORT2, &report_1[strlen(report_1)], NULL, NULL,
NULL, NULL, NULL);
/* 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;
2001-05-23 15:26:42 +02:00
}
perf_format(&perf_before, &perf_after, report_1, statistics, NULL);
2001-05-23 15:26:42 +02:00
sprintf(Print_buffer, "%s%s", statistics, NEWLINE);
ISQL_printf(isqlGlob.Out, Print_buffer);
2001-05-23 15:26:42 +02:00
}
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);
}
static void CLIB_ROUTINE query_abort(int)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* q u e r y _ a b o r t
*
**************************************
*
* Functional description
* Signal handler for interrupting output of a query.
*
**************************************/
2004-12-09 03:50:47 +01:00
Abort_flag = true;
2001-05-23 15:26:42 +02:00
}
// CVC: There's something either wrong or on purpose in this routine:
// it doesn't unescape the embedded quotes that may exist.
2003-09-09 13:03:37 +02:00
static void strip_quotes(const TEXT* in,
TEXT* out)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s t r i p _ q u o t e s
*
**************************************
*
* Functional description
* Get rid of quotes around strings
*
**************************************/
if (!in || !*in) {
*out = 0;
return;
}
2003-09-09 13:03:37 +02:00
TEXT quote = 0;
// Skip any initial quote
2001-05-23 15:26:42 +02:00
if ((*in == DBL_QUOTE) || (*in == SINGLE_QUOTE))
quote = *in++;
2003-09-09 13:03:37 +02:00
const TEXT* p = in;
2001-05-23 15:26:42 +02:00
// Now copy characters until we see the same quote or EOS
2001-05-23 15:26:42 +02:00
while (*p && (*p != quote)) {
*out++ = *p++;
}
*out = 0;
}
#ifdef DEV_BUILD
static const char* sqltype_to_string(USHORT sqltype)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s q l t y p e _ t o _ s t r i n g
*
**************************************
*
* Functional description
* Return a more readable version of SQLDA.sqltype
*
**************************************/
switch (sqltype & ~1) {
case SQL_TEXT:
return "TEXT ";
2001-05-23 15:26:42 +02:00
case SQL_VARYING:
return "VARYING ";
2001-05-23 15:26:42 +02:00
case SQL_SHORT:
return "SHORT ";
2001-05-23 15:26:42 +02:00
case SQL_LONG:
return "LONG ";
2001-05-23 15:26:42 +02:00
case SQL_INT64:
return "INT64 ";
2001-05-23 15:26:42 +02:00
case SQL_FLOAT:
return "FLOAT ";
2001-05-23 15:26:42 +02:00
case SQL_DOUBLE:
return "DOUBLE ";
2001-05-23 15:26:42 +02:00
case SQL_D_FLOAT:
return "D_FLOAT ";
2001-05-23 15:26:42 +02:00
case SQL_TIMESTAMP:
return "TIMESTAMP";
2001-05-23 15:26:42 +02:00
case SQL_TYPE_DATE:
return "SQL DATE ";
2001-05-23 15:26:42 +02:00
case SQL_TYPE_TIME:
return "TIME ";
2001-05-23 15:26:42 +02:00
case SQL_BLOB:
return "BLOB ";
2001-05-23 15:26:42 +02:00
case SQL_ARRAY:
return "ARRAY ";
2001-05-23 15:26:42 +02:00
case SQL_QUAD:
return "QUAD ";
2001-05-23 15:26:42 +02:00
default:
return "unknown ";
2001-05-23 15:26:42 +02:00
}
}
#endif
2003-09-09 13:03:37 +02:00