mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 06:43:04 +01:00
610 lines
14 KiB
C++
610 lines
14 KiB
C++
/*
|
|
* PROGRAM: JRD Command Oriented Query Language
|
|
* MODULE: command.cpp
|
|
* DESCRIPTION: Interprete commands
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "../jrd/y_ref.h"
|
|
#include "../jrd/ibase.h"
|
|
#include "../qli/dtr.h"
|
|
#include "../qli/parse.h"
|
|
#include "../qli/compile.h"
|
|
#include "../qli/exe.h"
|
|
#include "../jrd/license.h"
|
|
#include "../qli/all_proto.h"
|
|
#include "../qli/err_proto.h"
|
|
#include "../qli/exe_proto.h"
|
|
#include "../qli/meta_proto.h"
|
|
#include "../qli/proc_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#ifdef VMS
|
|
#include <descrip.h>
|
|
#endif
|
|
|
|
static void dump_procedure(DBB, IB_FILE*, const TEXT*, USHORT, FRBRD *);
|
|
static void extract_procedure(IB_FILE*, const TEXT*, USHORT, DBB, SLONG *);
|
|
|
|
extern USHORT QLI_lines, QLI_columns, QLI_form_mode, QLI_name_columns;
|
|
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
static SCHAR db_items[] =
|
|
{ gds_info_page_size, gds_info_allocation, gds_info_end };
|
|
#endif
|
|
|
|
int CMD_check_ready(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ c h e c k _ r e a d y
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Make sure at least one database is ready. If not, give a
|
|
* message.
|
|
*
|
|
**************************************/
|
|
|
|
if (QLI_databases)
|
|
return FALSE;
|
|
|
|
ERRQ_msg_put(95, NULL, NULL, NULL, NULL, NULL); // Msg95 No databases are currently ready
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CMD_copy_procedure( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ c o p y _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Copy one procedure to another, possibly
|
|
* across databases
|
|
*
|
|
**************************************/
|
|
QPR old_proc = (QPR) node->syn_arg[0];
|
|
QPR new_proc = (QPR) node->syn_arg[1];
|
|
|
|
PRO_copy_procedure(old_proc->qpr_database,
|
|
old_proc->qpr_name->nam_string,
|
|
new_proc->qpr_database,
|
|
new_proc->qpr_name->nam_string);
|
|
}
|
|
|
|
|
|
void CMD_define_procedure( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ d e f i n e _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Define a procedure in the named database
|
|
* or in the most recently readied database.
|
|
*
|
|
**************************************/
|
|
QPR proc = (QPR) node->syn_arg[0];
|
|
|
|
if (!(proc->qpr_database))
|
|
proc->qpr_database = QLI_databases;
|
|
|
|
PRO_create(proc->qpr_database, proc->qpr_name->nam_string);
|
|
}
|
|
|
|
|
|
void CMD_delete_proc( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ d e l e t e _ p r o c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Delete a procedure in the named database
|
|
* or in the most recently readied database.
|
|
*
|
|
**************************************/
|
|
QPR proc = (QPR) node->syn_arg[0];
|
|
|
|
if (!proc->qpr_database)
|
|
proc->qpr_database = QLI_databases;
|
|
|
|
if (PRO_delete_procedure(proc->qpr_database, proc->qpr_name->nam_string))
|
|
return;
|
|
|
|
ERRQ_msg_put(88, proc->qpr_name->nam_string, // Msg88 Procedure %s not found in database %s
|
|
proc->qpr_database->dbb_symbol->sym_string, NULL, NULL,
|
|
NULL);
|
|
}
|
|
|
|
|
|
void CMD_edit_proc( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ e d i t _ p r o c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Edit a procedure in the specified database.
|
|
*
|
|
**************************************/
|
|
QPR proc = (QPR) node->syn_arg[0];
|
|
if (!proc->qpr_database)
|
|
proc->qpr_database = QLI_databases;
|
|
|
|
PRO_edit_procedure(proc->qpr_database, proc->qpr_name->nam_string);
|
|
}
|
|
|
|
|
|
void CMD_extract( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ e x t r a c t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Extract a series of procedures.
|
|
*
|
|
**************************************/
|
|
DBB database;
|
|
|
|
IB_FILE* file = (IB_FILE*) EXEC_open_output((QLI_NOD) node->syn_arg[1]);
|
|
|
|
SYN list = node->syn_arg[0];
|
|
if (list) {
|
|
SYN* ptr = list->syn_arg;
|
|
for (SYN* const end = ptr + list->syn_count; ptr < end; ptr++) {
|
|
QPR proc = (QPR) *ptr;
|
|
if (!(database = proc->qpr_database))
|
|
database = QLI_databases;
|
|
NAM name = proc->qpr_name;
|
|
FRBRD* blob = PRO_fetch_procedure(database, name->nam_string);
|
|
if (!blob) {
|
|
ERRQ_msg_put(89, // Msg89 Procedure %s not found in database %s
|
|
name->nam_string,
|
|
database->dbb_symbol->sym_string, NULL, NULL,
|
|
NULL);
|
|
continue;
|
|
}
|
|
dump_procedure(database, file, name->nam_string, name->nam_length,
|
|
blob);
|
|
}
|
|
}
|
|
else {
|
|
CMD_check_ready();
|
|
for (database = QLI_databases; database;
|
|
database =
|
|
database->dbb_next)
|
|
{
|
|
PRO_scan(database, (void (*)()) extract_procedure, file);
|
|
}
|
|
}
|
|
|
|
#ifdef WIN_NT
|
|
if (((QLI_NOD) node->syn_arg[1])->nod_arg[e_out_pipe])
|
|
_pclose(file);
|
|
else
|
|
#endif
|
|
ib_fclose(file);
|
|
}
|
|
|
|
|
|
void CMD_finish( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ f i n i s h
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Perform FINISH. Either finish listed databases or everything.
|
|
*
|
|
**************************************/
|
|
if (node->syn_count == 0) {
|
|
while (QLI_databases)
|
|
MET_finish(QLI_databases);
|
|
return;
|
|
}
|
|
|
|
for (USHORT i = 0; i < node->syn_count; i++)
|
|
MET_finish((DBB) node->syn_arg[i]);
|
|
}
|
|
|
|
|
|
void CMD_rename_proc( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ r e n a m e _ p r o c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Rename a procedure in the named database,
|
|
* or the most recently readied database.
|
|
*
|
|
**************************************/
|
|
QPR old_proc = (QPR) node->syn_arg[0];
|
|
QPR new_proc = (QPR) node->syn_arg[1];
|
|
|
|
DBB database = old_proc->qpr_database;
|
|
if (!database)
|
|
database = QLI_databases;
|
|
|
|
if (new_proc->qpr_database && (new_proc->qpr_database != database))
|
|
IBERROR(84); // Msg84 Procedures can not be renamed across databases. Try COPY
|
|
NAM old_name = old_proc->qpr_name;
|
|
NAM new_name = new_proc->qpr_name;
|
|
|
|
if (PRO_rename_procedure
|
|
(database, old_name->nam_string, new_name->nam_string))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ERRQ_error(85, // Msg85 Procedure %s not found in database %s
|
|
old_name->nam_string, database->dbb_symbol->sym_string, NULL,
|
|
NULL, NULL);
|
|
}
|
|
|
|
|
|
void CMD_set( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ s e t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Set various options.
|
|
*
|
|
**************************************/
|
|
USHORT length;
|
|
CON string;
|
|
TEXT *name;
|
|
|
|
SYN* ptr = node->syn_arg;
|
|
|
|
for (USHORT i = 0; i < node->syn_count; i++) {
|
|
USHORT foo = (USHORT)(ULONG) *ptr++;
|
|
const enum set_t sw = (enum set_t) foo;
|
|
SYN value = *ptr++;
|
|
switch (sw) {
|
|
case set_blr:
|
|
QLI_blr = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_statistics:
|
|
QLI_statistics = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_columns:
|
|
QLI_name_columns = QLI_columns = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_lines:
|
|
QLI_lines = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_semi:
|
|
QLI_semi = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_echo:
|
|
QLI_echo = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_form:
|
|
IBERROR(484); // FORMs not supported
|
|
break;
|
|
|
|
case set_password:
|
|
string = (CON) value;
|
|
length =
|
|
MIN(string->con_desc.dsc_length,
|
|
sizeof(QLI_default_password));
|
|
strncpy(QLI_default_password, (char*) string->con_data, length);
|
|
QLI_default_password[length] = 0;
|
|
break;
|
|
|
|
case set_prompt:
|
|
string = (CON) value;
|
|
if (string->con_desc.dsc_length > sizeof(QLI_prompt_string))
|
|
ERRQ_error(86, NULL, NULL, NULL, NULL, NULL); // Msg86 substitute prompt string too long
|
|
strncpy(QLI_prompt_string, (char*) string->con_data,
|
|
string->con_desc.dsc_length);
|
|
QLI_prompt_string[string->con_desc.dsc_length] = 0;
|
|
break;
|
|
|
|
case set_continuation:
|
|
string = (CON) value;
|
|
if (string->con_desc.dsc_length > sizeof(QLI_cont_string))
|
|
ERRQ_error(87, NULL, NULL, NULL, NULL, NULL); // Msg87 substitute prompt string too long
|
|
strncpy(QLI_cont_string, (char*) string->con_data,
|
|
string->con_desc.dsc_length);
|
|
QLI_cont_string[string->con_desc.dsc_length] = 0;
|
|
break;
|
|
|
|
case set_matching_language:
|
|
if (QLI_matching_language)
|
|
ALLQ_release((FRB) QLI_matching_language);
|
|
if (!(string = (CON) value)) {
|
|
QLI_matching_language = NULL;
|
|
break;
|
|
}
|
|
QLI_matching_language =
|
|
(CON) ALLOCPV(type_con, string->con_desc.dsc_length);
|
|
strncpy((char*)QLI_matching_language->con_data, (char*)string->con_data,
|
|
string->con_desc.dsc_length);
|
|
QLI_matching_language->con_desc.dsc_dtype = dtype_text;
|
|
QLI_matching_language->con_desc.dsc_address =
|
|
QLI_matching_language->con_data;
|
|
QLI_matching_language->con_desc.dsc_length =
|
|
string->con_desc.dsc_length;
|
|
break;
|
|
|
|
case set_user:
|
|
string = (CON) value;
|
|
length =
|
|
MIN(string->con_desc.dsc_length, sizeof(QLI_default_user));
|
|
strncpy(QLI_default_user, (char*)string->con_data, length);
|
|
QLI_default_user[length] = 0;
|
|
break;
|
|
|
|
break;
|
|
|
|
case set_count:
|
|
QLI_count = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_charset:
|
|
if (!value) {
|
|
QLI_charset[0] = 0;
|
|
break;
|
|
}
|
|
name = ((NAM) value)->nam_string;
|
|
length = MIN(strlen(name), sizeof(QLI_charset));
|
|
strncpy(QLI_charset, name, length);
|
|
QLI_charset[length] = 0;
|
|
break;
|
|
|
|
#ifdef DEV_BUILD
|
|
case set_explain:
|
|
QLI_explain = (USHORT)(ULONG) value;
|
|
break;
|
|
|
|
case set_hex_output:
|
|
QLI_hex_output = (USHORT)(ULONG) value;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
BUGCHECK(6); // Msg6 set option not implemented
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CMD_shell( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ s h e l l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Invoke operating system shell.
|
|
*
|
|
**************************************/
|
|
TEXT buffer[256];
|
|
USHORT l;
|
|
|
|
// Copy command, inserting extra blank at end.
|
|
|
|
TEXT* p = buffer;
|
|
CON constant = (CON) node->syn_arg[0];
|
|
if (constant) {
|
|
const TEXT* q = (TEXT*) constant->con_data;
|
|
if (l = constant->con_desc.dsc_length)
|
|
do {
|
|
*p++ = *q++;
|
|
} while (--l);
|
|
*p++ = ' ';
|
|
*p = 0;
|
|
}
|
|
else
|
|
#ifndef WIN_NT
|
|
strcpy(buffer, "$SHELL");
|
|
#else
|
|
strcpy(buffer, "%ComSpec%");
|
|
#endif
|
|
|
|
#ifdef VMS
|
|
struct dsc$descriptor desc, *ptr;
|
|
if (constant) {
|
|
desc.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
desc.dsc$b_class = DSC$K_CLASS_S;
|
|
desc.dsc$w_length = p - buffer;
|
|
desc.dsc$a_pointer = buffer;
|
|
ptr = &desc;
|
|
}
|
|
else
|
|
ptr = NULL;
|
|
int return_status = 0;
|
|
int mask = 1;
|
|
int status = lib$spawn(ptr, // Command to be executed
|
|
NULL, // Command file
|
|
NULL, // Output file
|
|
&mask, // sub-process characteristics mask
|
|
NULL, // sub-process name
|
|
NULL, // returned process id
|
|
&return_status, // completion status
|
|
&15); // event flag for completion
|
|
if (status & 1)
|
|
while (!return_status)
|
|
sys$waitfr(15);
|
|
#else
|
|
system(buffer);
|
|
#endif
|
|
}
|
|
|
|
|
|
void CMD_transaction( SYN node)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C M D _ t r a n s a c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Perform COMMIT, ROLLBACK or PREPARE
|
|
* on listed databases or everything.
|
|
*
|
|
**************************************/
|
|
|
|
/* If there aren't any open databases then obviously
|
|
there isn't anything to commit. */
|
|
|
|
if (node->syn_count == 0 && !QLI_databases)
|
|
return;
|
|
|
|
if (node->syn_type == nod_commit)
|
|
if ((node->syn_count > 1) ||
|
|
(node->syn_count == 0 && QLI_databases->dbb_next))
|
|
{
|
|
node->syn_type = nod_prepare;
|
|
CMD_transaction(node);
|
|
node->syn_type = nod_commit;
|
|
}
|
|
else if (node->syn_count == 1) {
|
|
DBB tmp_db = (DBB) node->syn_arg[0];
|
|
tmp_db->dbb_flags |= DBB_prepared;
|
|
}
|
|
else
|
|
QLI_databases->dbb_flags |= DBB_prepared;
|
|
|
|
|
|
if (node->syn_count == 0) {
|
|
for (DBB db_iter = QLI_databases; db_iter; db_iter = db_iter->dbb_next)
|
|
{
|
|
if ((node->syn_type == nod_commit)
|
|
&& !(db_iter->dbb_flags & DBB_prepared))
|
|
{
|
|
ERRQ_msg_put(465, db_iter->dbb_symbol->sym_string,
|
|
NULL, NULL, NULL, NULL);
|
|
}
|
|
else if (node->syn_type == nod_prepare)
|
|
db_iter->dbb_flags |= DBB_prepared;
|
|
if (db_iter->dbb_transaction)
|
|
MET_transaction(node->syn_type, db_iter);
|
|
if (db_iter->dbb_meta_trans)
|
|
MET_meta_commit(db_iter);
|
|
if (db_iter->dbb_proc_trans)
|
|
PRO_commit(db_iter);
|
|
}
|
|
return;
|
|
}
|
|
|
|
SYN* ptr = node->syn_arg;
|
|
for (SYN* const end = ptr + node->syn_count; ptr < end; ptr++) {
|
|
DBB database = (DBB) *ptr;
|
|
if ((node->syn_type == nod_commit) &&
|
|
!(database->dbb_flags & DBB_prepared))
|
|
{
|
|
ERRQ_msg_put(465, database->dbb_symbol->sym_string, NULL,
|
|
NULL, NULL, NULL);
|
|
}
|
|
else if (node->syn_type == nod_prepare)
|
|
database->dbb_flags |= DBB_prepared;
|
|
if (database->dbb_transaction)
|
|
MET_transaction(node->syn_type, database);
|
|
}
|
|
}
|
|
|
|
|
|
static void dump_procedure(
|
|
DBB database,
|
|
IB_FILE* file,
|
|
const TEXT* name, USHORT length, FRBRD *blob)
|
|
{
|
|
/**************************************
|
|
*
|
|
* d u m p _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Extract a procedure from a database.
|
|
*
|
|
**************************************/
|
|
TEXT buffer[256];
|
|
|
|
ib_fprintf(file, "DELETE PROCEDURE %.*s;\n", length, name);
|
|
ib_fprintf(file, "DEFINE PROCEDURE %.*s\n", length, name);
|
|
|
|
while (PRO_get_line(blob, buffer, sizeof(buffer)))
|
|
ib_fputs(buffer, file);
|
|
|
|
PRO_close(database, blob);
|
|
ib_fprintf(file, "END_PROCEDURE\n\n");
|
|
}
|
|
|
|
|
|
static void extract_procedure(
|
|
IB_FILE* file,
|
|
const TEXT* name,
|
|
USHORT length, DBB database, SLONG* blob_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* e x t r a c t _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Extract a procedure from a database.
|
|
*
|
|
**************************************/
|
|
FRBRD* blob = PRO_open_blob(database, blob_id);
|
|
dump_procedure(database, file, name, length, blob);
|
|
}
|
|
|
|
|