static void execute_abort( qli_nod* node) { /************************************** * * e x e c u t e _ a b o r t * ************************************** * * Functional description * Abort a statement. * **************************************/ if (node->nod_count) { const TEXT* ptr = NULL; Firebird::VaryStr<80> temp; const USHORT l = MOVQ_get_string(EVAL_value(node->nod_arg[0]), &ptr, &temp, sizeof(temp)); UCHAR msg[128]; MOVQ_terminate(ptr, (SCHAR*) msg, l, sizeof(msg)); ERRQ_error(40, SafeArg() << msg); // Msg40 Request terminated by statement: %s } IBERROR(41); // Msg41 Request terminated by statement }
void CMD_rename_proc( qli_syntax* 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. * **************************************/ qli_proc* old_proc = (qli_proc*) node->syn_arg[0]; qli_proc* new_proc = (qli_proc*) node->syn_arg[1]; qli_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 const qli_name* old_name = old_proc->qpr_name; const qli_name* new_name = new_proc->qpr_name; if (PRO_rename_procedure(database, old_name->nam_string, new_name->nam_string)) { return; } ERRQ_error(85, SafeArg() << old_name->nam_string << database->dbb_symbol->sym_string); // Msg85 Procedure %s not found in database %s }
static bool reconnect(FB_API_HANDLE handle, SLONG number, const TEXT* name, SINT64 switches) { ISC_STATUS_ARRAY status_vector; const SLONG id = gds__vax_integer(reinterpret_cast<const UCHAR*>(&number), 4); FB_API_HANDLE transaction = 0; if (isc_reconnect_transaction(status_vector, &handle, &transaction, sizeof(id), reinterpret_cast<const char*>(&id))) { ALICE_print(90, SafeArg() << name); // msg 90: failed to reconnect to a transaction in database %s ALICE_print_status(true, status_vector); return true; } if (!(switches & (sw_commit | sw_rollback))) { ALICE_print(91, SafeArg() << number); // msg 91: Transaction %ld: switches = ask(); if (switches == ~SINT64(0)) { ALICE_print(84); // msg 84: unexpected end of input return true; } } if (switches & sw_commit) isc_commit_transaction(status_vector, &transaction); else if (switches & sw_rollback) isc_rollback_transaction(status_vector, &transaction); else return false; if (status_vector[1]) { ALICE_print_status(true, status_vector); return true; } return false; }
void CMD_extract( qli_syntax* node) { /************************************** * * C M D _ e x t r a c t * ************************************** * * Functional description * Extract a series of procedures. * **************************************/ FILE* file = (FILE*) EXEC_open_output((qli_nod*) node->syn_arg[1]); qli_syntax* list = node->syn_arg[0]; if (list) { qli_syntax** ptr = list->syn_arg; for (const qli_syntax* const* const end = ptr + list->syn_count; ptr < end; ptr++) { qli_proc* proc = (qli_proc*) *ptr; qli_dbb* database = proc->qpr_database; if (!database) database = QLI_databases; const qli_name* name = proc->qpr_name; FB_API_HANDLE blob = PRO_fetch_procedure(database, name->nam_string); if (!blob) { ERRQ_msg_put(89, SafeArg() << name->nam_string << database->dbb_symbol->sym_string); // Msg89 Procedure %s not found in database %s continue; } dump_procedure(database, file, name->nam_string, name->nam_length, blob); } } else { CMD_check_ready(); for (qli_dbb* database = QLI_databases; database; database = database->dbb_next) { PRO_scan(database, extract_procedure, file); } } #ifdef WIN_NT if (((qli_nod*) node->syn_arg[1])->nod_arg[e_out_pipe]) _pclose(file); else #endif fclose(file); }
static bool yes_no(USHORT number, const TEXT* arg1) { /************************************** * * y e s _ n o * ************************************** * * Functional description * Put out a prompt that expects a yes/no * answer, and keep trying until we get an * acceptable answer (e.g. y, Yes, N, etc.) * **************************************/ TEXT prompt[256]; ERRQ_msg_format(number, sizeof(prompt), prompt, SafeArg() << arg1); if (!yes_no_loaded) { yes_no_loaded = true; // Msg498 NO if (!ERRQ_msg_get(498, answer_table[0].answer, sizeof(answer_table[0].answer))) strcpy(answer_table[0].answer, "NO"); // default if msg_get fails // Msg497 YES if (!ERRQ_msg_get(497, answer_table[1].answer, sizeof(answer_table[1].answer))) strcpy(answer_table[1].answer, "YES"); } TEXT buffer[256]; while (true) { buffer[0] = 0; if (!LEX_get_line(prompt, buffer, sizeof(buffer))) return true; for (const answer_t* response = answer_table; *response->answer != '\0'; response++) { const TEXT* p = buffer; while (*p == ' ') p++; if (*p == EOF) return true; for (const TEXT* q = response->answer; *p && UPPER(*p) == *q++; p++) ; if (!*p || *p == '\n') return response->value; } } }
void ERRQ_print_error(USHORT number, const char* str) { /************************************** * * E R R Q _ p r i n t _ e r r o r * ************************************** * * Functional description * An error has occurred. Put out an error message and * unwind. * **************************************/ ERRQ_error(number, SafeArg() << str); }
void ERRQ_msg_put(USHORT number, const char* str) { /************************************** * * E R R Q _ m s g _ p u t * ************************************** * * Functional description * Retrieve a message from the error file, format it, and print it * It's same outcome as ERRQ_msg_partial but with a newline at the end. * **************************************/ fb_msg_format(0, QLI_MSG_FAC, number, sizeof(ERRQ_message), ERRQ_message, SafeArg() << str); printf("%s\n", ERRQ_message); }
void ERRQ_error(USHORT number, const char* str) { /************************************** * * E R R Q _ e r r o r * ************************************** * * Functional description * An error has occurred. Put out an error message and * unwind. If this was called before the unwind path * was established, don't unwind just print error and exit. * **************************************/ ERRQ_error(number, SafeArg() << str); }
void ERRQ_bugcheck( USHORT number) { /************************************** * * E R R Q _ b u g c h e c k * ************************************** * * Functional description * Somebody has screwed up. Bugcheck. * **************************************/ TEXT s[256]; ERRQ_msg_format(number, sizeof(s), s); ERRQ_error(9, SafeArg() << s); // Msg9 INTERNAL: %s }
bool TDR_attach_database(ISC_STATUS* status_vector, tdr* trans, const TEXT* pathname) { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); if (tdgbl->ALICE_data.ua_debug) ALICE_print(68, SafeArg() << pathname); // msg 68: ATTACH_DATABASE: attempted attach of %s Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); dpb.insertTag(isc_dpb_no_garbage_collect); dpb.insertTag(isc_dpb_gfix_attach); tdgbl->uSvc->fillDpb(dpb); if (tdgbl->ALICE_data.ua_user) { dpb.insertString(isc_dpb_user_name, tdgbl->ALICE_data.ua_user, fb_strlen(tdgbl->ALICE_data.ua_user)); } if (tdgbl->ALICE_data.ua_password) { dpb.insertString(tdgbl->uSvc->isService() ? isc_dpb_password_enc : isc_dpb_password, tdgbl->ALICE_data.ua_password, fb_strlen(tdgbl->ALICE_data.ua_password)); } trans->tdr_db_handle = 0; isc_attach_database(status_vector, 0, pathname, &trans->tdr_db_handle, dpb.getBufferLength(), reinterpret_cast<const char*>(dpb.getBuffer())); if (status_vector[1]) { if (tdgbl->ALICE_data.ua_debug) { ALICE_print(69); // msg 69: failed ALICE_print_status(false, status_vector); } return false; } MET_set_capabilities(status_vector, trans); if (tdgbl->ALICE_data.ua_debug) ALICE_print(70); // msg 70: succeeded return true; }
void ERRQ_syntax( USHORT number) { /************************************** * * E R R Q _ s y n t a x * ************************************** * * Functional description * Syntax error has occurred. Give some hint of what went * wrong. * **************************************/ TEXT s[256]; ERRQ_msg_format(number, sizeof(s), s); ERRQ_error(13, SafeArg() << s << QLI_token->tok_string); // Msg13 expected %s, encountered %s }
void ERRQ_error_format(USHORT number, const SafeArg& arg) { /************************************** * * E R R Q _ e r r o r _ f o r m a t * ************************************** * * Functional description * Retrieve a message from the error file and format it * in the standard qli error format, put it where * ERRQ_pending expects to find it. **************************************/ TEXT s[256]; fb_msg_format(0, QLI_MSG_FAC, number, sizeof(s), s, arg); fb_msg_format(0, QLI_MSG_FAC, 12, sizeof(ERRQ_message), ERRQ_message, SafeArg() << s); // Msg12 ** QLI error: %s ** QLI_error = ERRQ_message; QLI_skip_line = true; }
void CMD_delete_proc( qli_syntax* 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. * **************************************/ qli_proc* proc = (qli_proc*) 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, SafeArg() << proc->qpr_name->nam_string << // Msg88 Procedure %s not found in database %s proc->qpr_database->dbb_symbol->sym_string); }
int alice(Firebird::UtilSvc* uSvc) { AliceGlobals gblInstance(uSvc); AliceGlobals* tdgbl = &gblInstance; AliceGlobals::putSpecific(tdgbl); int exit_code = FINI_ERROR; try { // Perform some special handling when run as a Firebird service. The // first switch can be "-svc" (lower case!) or it can be "-svc_re" followed // by 3 file descriptors to use in re-directing stdin, stdout, and stderr. tdgbl->ALICE_data.ua_user = NULL; tdgbl->ALICE_data.ua_password = NULL; #ifdef TRUSTED_AUTH tdgbl->ALICE_data.ua_trusted = false; #endif tdgbl->ALICE_data.ua_tr_user = NULL; tdgbl->ALICE_data.ua_tr_role = false; // Start by parsing switches bool error = false, help = false; SINT64 flags = 0; tdgbl->ALICE_data.ua_shutdown_delay = 0; const TEXT* database = NULL; TEXT string[512]; const char** argv = uSvc->argv.begin(); int argc = uSvc->argv.getCount(); ++argv; // tested outside the loop const in_sw_tab_t* table = alice_in_sw_table; while (--argc > 0) { if ((*argv)[0] != '-') { if (database) { ALICE_error(1, SafeArg() << database); // msg 1: "data base file name (%s) already given", } database = *argv++; continue; } ALICE_down_case(*argv++, string, sizeof(string)); if (!string[1]) { continue; } if (strcmp(string, "-?") == 0) { error = help = true; break; } for (table = alice_in_sw_table; true; ++table) { const TEXT* p = (TEXT*) table->in_sw_name; if (!p) { ALICE_print(2, SafeArg() << (*--argv)); // msg 2: invalid switch %s error = true; break; } TEXT* q = &string[1]; while (*q && *p++ == *q) { q++; } if (!*q && (q - &string[1] >= table->in_sw_min_length)) { break; } } if (error) { break; } if (*table->in_sw_name == 'x') { tdgbl->ALICE_data.ua_debug++; } if (table->in_sw_value & sw_trusted_svc) { uSvc->checkService(); if (--argc <= 0) { ALICE_error(13); // msg 13: user name required } tdgbl->ALICE_data.ua_tr_user = *argv++; continue; } if (table->in_sw_value & sw_trusted_role) { uSvc->checkService(); tdgbl->ALICE_data.ua_tr_role = true; continue; } #ifdef TRUSTED_AUTH if (table->in_sw_value & sw_trusted_auth) { tdgbl->ALICE_data.ua_trusted = true; continue; } #endif if (table->in_sw_value == sw_z) { ALICE_print(3, SafeArg() << GDS_VERSION); // msg 3: gfix version %s } if ((table->in_sw_incompatibilities & flags) || (table->in_sw_requires && !(table->in_sw_requires & flags))) { ALICE_print(4); // msg 4: incompatible switch combination error = true; break; } flags |= table->in_sw_value; if ((table->in_sw_value & (sw_shut | sw_online)) && (argc > 1)) { ALICE_down_case(*argv, string, sizeof(string)); bool found = true; if (strcmp(string, "normal") == 0) tdgbl->ALICE_data.ua_shutdown_mode = SHUT_NORMAL; else if (strcmp(string, "multi") == 0) tdgbl->ALICE_data.ua_shutdown_mode = SHUT_MULTI; else if (strcmp(string, "single") == 0) tdgbl->ALICE_data.ua_shutdown_mode = SHUT_SINGLE; else if (strcmp(string, "full") == 0) tdgbl->ALICE_data.ua_shutdown_mode = SHUT_FULL; else found = false; // Consume argument only if we identified mode // Let's hope that database with names of modes above are unusual if (found) { argv++; argc--; } } #ifdef DEV_BUILD /* if (table->in_sw_value & sw_begin_log) { if (--argc <= 0) { ALICE_error(5); // msg 5: replay log pathname required } fb_utils::copy_terminate(tdgbl->ALICE_data.ua_log_file, *argv++, sizeof(tdgbl->ALICE_data.ua_log_file)); } */ #endif if (table->in_sw_value & sw_buffers) { if (--argc <= 0) { ALICE_error(6); // msg 6: number of page buffers for cache required } ALICE_down_case(*argv++, string, sizeof(string)); if ((!(tdgbl->ALICE_data.ua_page_buffers = atoi(string))) && (strcmp(string, "0"))) { ALICE_error(7); // msg 7: numeric value required } if (tdgbl->ALICE_data.ua_page_buffers < 0) { ALICE_error(114); // msg 114: positive or zero numeric value required } } if (table->in_sw_value & sw_housekeeping) { if (--argc <= 0) { ALICE_error(9); // msg 9: number of transactions per sweep required } ALICE_down_case(*argv++, string, sizeof(string)); if ((!(tdgbl->ALICE_data.ua_sweep_interval = atoi(string))) && (strcmp(string, "0"))) { ALICE_error(7); // msg 7: numeric value required } if (tdgbl->ALICE_data.ua_sweep_interval < 0) { ALICE_error(114); // msg 114: positive or zero numeric value required } } if (table->in_sw_value & sw_set_db_dialect) { if (--argc <= 0) { ALICE_error(113); // msg 113: dialect number required } ALICE_down_case(*argv++, string, sizeof(string)); if ((!(tdgbl->ALICE_data.ua_db_SQL_dialect = atoi(string))) && (strcmp(string, "0"))) { ALICE_error(7); // msg 7: numeric value required } // JMB: Removed because tdgbl->ALICE_data.ua_db_SQL_dialect is // an unsigned number. Therefore this check is useless. // if (tdgbl->ALICE_data.ua_db_SQL_dialect < 0) // { // ALICE_error(114); // msg 114: positive or zero numeric value required // } } if (table->in_sw_value & (sw_commit | sw_rollback | sw_two_phase)) { if (--argc <= 0) { ALICE_error(10); // msg 10: transaction number or "all" required } ALICE_down_case(*argv++, string, sizeof(string)); if (!(tdgbl->ALICE_data.ua_transaction = atoi(string))) { if (strcmp(string, "all")) { ALICE_error(10); // msg 10: transaction number or "all" required } else { flags |= sw_list; } } } if (table->in_sw_value & sw_write) { if (--argc <= 0) { ALICE_error(11); // msg 11: "sync" or "async" required } ALICE_down_case(*argv++, string, sizeof(string)); if (!strcmp(string, ALICE_SW_SYNC)) { tdgbl->ALICE_data.ua_force = true; } else if (!strcmp(string, ALICE_SW_ASYNC)) { tdgbl->ALICE_data.ua_force = false; } else { ALICE_error(11); // msg 11: "sync" or "async" required } } if (table->in_sw_value & sw_no_reserve) { if (--argc <= 0) { ALICE_error(12); // msg 12: "full" or "reserve" required } ALICE_down_case(*argv++, string, sizeof(string)); if (!strcmp(string, "full")) { tdgbl->ALICE_data.ua_no_reserve = true; } else if (!strcmp(string, "reserve")) { tdgbl->ALICE_data.ua_no_reserve = false; } else { ALICE_error(12); // msg 12: "full" or "reserve" required } } if (table->in_sw_value & sw_user) { if (--argc <= 0) { ALICE_error(13); // msg 13: user name required } tdgbl->ALICE_data.ua_user = *argv++; } if (table->in_sw_value & sw_password) { if (--argc <= 0) { ALICE_error(14); // msg 14: password required } uSvc->hidePasswd(uSvc->argv, argv - uSvc->argv.begin()); tdgbl->ALICE_data.ua_password = *argv++; } if (table->in_sw_value & sw_fetch_password) { if (--argc <= 0) { ALICE_error(14); // msg 14: password required } switch (fb_utils::fetchPassword(*argv, tdgbl->ALICE_data.ua_password)) { case fb_utils::FETCH_PASS_OK: break; case fb_utils::FETCH_PASS_FILE_OPEN_ERROR: ALICE_error(116, MsgFormat::SafeArg() << *argv << errno); // error @2 opening password file @1 break; case fb_utils::FETCH_PASS_FILE_READ_ERROR: ALICE_error(117, MsgFormat::SafeArg() << *argv << errno); // error @2 reading password file @1 break; case fb_utils::FETCH_PASS_FILE_EMPTY: ALICE_error(118, MsgFormat::SafeArg() << *argv); // password file @1 is empty break; } ++argv; } if (table->in_sw_value & sw_disable) { if (--argc <= 0) { ALICE_error(15); // msg 15: subsystem name } ALICE_down_case(*argv++, string, sizeof(string)); if (strcmp(string, "wal")) { ALICE_error(16); // msg 16: "wal" required } } if (table->in_sw_value & (sw_attach | sw_force | sw_tran | sw_cache)) { if (--argc <= 0) { ALICE_error(17); // msg 17: number of seconds required } ALICE_down_case(*argv++, string, sizeof(string)); if ((!(tdgbl->ALICE_data.ua_shutdown_delay = atoi(string))) && (strcmp(string, "0"))) { ALICE_error(7); // msg 7: numeric value required } if (tdgbl->ALICE_data.ua_shutdown_delay < 0 || tdgbl->ALICE_data.ua_shutdown_delay > 32767) { ALICE_error(18); // msg 18: numeric value between 0 and 32767 inclusive required } } if (table->in_sw_value & sw_mode) { if (--argc <= 0) { ALICE_error(110); // msg 110: "read_only" or "read_write" required } ALICE_down_case(*argv++, string, sizeof(string)); if (!strcmp(string, ALICE_SW_MODE_RO)) { tdgbl->ALICE_data.ua_read_only = true; } else if (!strcmp(string, ALICE_SW_MODE_RW)) { tdgbl->ALICE_data.ua_read_only = false; } else { ALICE_error(110); // msg 110: "read_only" or "read_write" required } } } // put this here since to put it above overly complicates the parsing // can't use tbl_requires since it only looks backwards on command line if ((flags & sw_shut) && !(flags & ((sw_attach | sw_force | sw_tran | sw_cache)))) { ALICE_error(19); // msg 19: must specify type of shutdown } // catch the case where -z is only command line option // flags is unset since sw_z == 0 if (!flags && !error && table->in_sw_value == sw_z) { ALICE_exit(FINI_OK, tdgbl); } if (!flags || !(flags & ~(sw_user | sw_password | sw_fetch_password | sw_trusted_auth | sw_trusted_svc | sw_trusted_role))) { if (!help && !uSvc->isService()) { ALICE_print(20); // msg 20: please retry, specifying an option } error = true; } if (error) { if (uSvc->isService()) { uSvc->setServiceStatus(ALICE_MSG_FAC, 20, MsgFormat::SafeArg()); } else { if (help) ALICE_print(120); // usage: gfix [options] <database> ALICE_print(21); // msg 21: plausible options are: for (table = alice_in_sw_table; table->in_sw_name; table++) { if (table->in_sw_msg) ALICE_print(table->in_sw_msg); } ALICE_print(22); // msg 22: \n qualifiers show the major option in parenthesis } ALICE_exit(FINI_ERROR, tdgbl); } if (!database) { ALICE_error(23); // msg 23: please retry, giving a database name } // generate the database parameter block for the attach, // based on the various flags USHORT ret; if (flags & (sw_list | sw_commit | sw_rollback | sw_two_phase)) { ret = EXE_two_phase(database, flags); } else { ret = EXE_action(database, flags); const SLONG* ua_val_errors = tdgbl->ALICE_data.ua_val_errors; if (!ua_val_errors[VAL_INVALID_DB_VERSION]) { bool any_error = false; for (int i = 0; i < MAX_VAL_ERRORS; ++i) { if (ua_val_errors[i]) { any_error = true; break; } } if (any_error) { ALICE_print(24); // msg 24: Summary of validation errors\n for (int i = 0; i < MAX_VAL_ERRORS; ++i) { if (ua_val_errors[i]) { ALICE_print(val_err_table[i], SafeArg() << ua_val_errors[i]); } } } } } if (ret == FINI_ERROR) { ALICE_print_status(true, tdgbl->status); ALICE_exit(FINI_ERROR, tdgbl); } ALICE_exit(FINI_OK, tdgbl); } // try catch (const Firebird::LongJump&) { // All "calls" to ALICE_exit(), normal and error exits, wind up here exit_code = tdgbl->exit_code; } catch (const Firebird::Exception& e) { // Non-alice exception was caught e.stuff_exception(tdgbl->status_vector); ALICE_print_status(true, tdgbl->status_vector); exit_code = FINI_ERROR; } AliceGlobals::restoreSpecific(); #if defined(DEBUG_GDS_ALLOC) if (!uSvc->isService()) { gds_alloc_report(0, __FILE__, __LINE__); } #endif if ((exit_code != FINI_OK) && uSvc->isService()) { uSvc->initStatus(); uSvc->setServiceStatus(tdgbl->status); } tdgbl->uSvc->started(); return exit_code; }
qli_tok* LEX_token() { /************************************** * * L E X _ t o k e n * ************************************** * * Functional description * Parse and return the next token. * **************************************/ qli_tok* token = QLI_token; TEXT* p = token->tok_string; // Get next significant byte. If it's the last EOL of a blob, throw it away SSHORT c; for (;;) { c = skip_white(); if (c != '\n' || QLI_line->line_type != line_blob) break; qli_line* prior = QLI_line; next_line(true); if (prior == QLI_line) break; } // If we hit end of file, make up a phoney token if (!QLI_line) { const TEXT* q = eof_string; while (*p++ = *q++); token->tok_type = tok_eof; token->tok_keyword = KW_none; return NULL; } *p++ = c; QLI_token->tok_position = QLI_line->line_position + QLI_line->line_ptr - QLI_line->line_data - 1; // On end of file, generate furious but phone end of line tokens char char_class = classes(c); if (char_class & CHR_letter) { for (c = nextchar(true); classes(c) & CHR_ident; c = nextchar(true)) *p++ = c; retchar(); token->tok_type = tok_ident; } else if (((char_class & CHR_digit) || c == '.') && scan_number(c, &p)) token->tok_type = tok_number; else if (char_class & CHR_quote) { token->tok_type = tok_quoted; while (true) { const SSHORT next = nextchar(false); if (!next || next == '\n') { retchar(); IBERROR(63); // Msg 63 unterminated quoted string break; } *p++ = next; if ((p - token->tok_string) >= MAXSYMLEN) ERRQ_msg_put(470, SafeArg() << MAXSYMLEN); // Msg 470 literal too long // If there are 2 quotes in a row, interpret 2nd as a literal if (next == c) { const SSHORT peek = nextchar(false); retchar(); if (peek != c) break; nextchar(false); } } } else if (c == '\n') { // end of line, signal it properly with a phoney token. token->tok_type = tok_eol; --p; const TEXT* q = eol_string; while (*q) *p++ = *q++; } else { token->tok_type = tok_punct; *p++ = nextchar(true); if (!HSH_lookup(token->tok_string, 2)) { retchar(); --p; } } token->tok_length = p - token->tok_string; *p = '\0'; if (token->tok_string[0] == '$' && trans_limit < TRANS_LIMIT) { Firebird::string s; if (fb_utils::readenv(token->tok_string + 1, s)) { LEX_push_string(s.c_str()); ++trans_limit; token = LEX_token(); --trans_limit; return token; } } qli_symbol* symbol = HSH_lookup(token->tok_string, token->tok_length); token->tok_symbol = symbol; if (symbol && symbol->sym_type == SYM_keyword) token->tok_keyword = (kwwords) symbol->sym_keyword; else token->tok_keyword = KW_none; if (sw_trace) puts(token->tok_string); return token; }
static void reattach_database(tdr* trans) { ISC_STATUS_ARRAY status_vector; char buffer[1024]; // sizeof(buffer) - 1 => leave space for the terminator. const char* const end = buffer + sizeof(buffer) - 1; AliceGlobals* tdgbl = AliceGlobals::getSpecific(); ISC_get_host(buffer, sizeof(buffer)); if (trans->tdr_fullpath) { // if this is being run from the same host, // try to reconnect using the same pathname if (!strcmp(buffer, reinterpret_cast<const char*>(trans->tdr_host_site->str_data))) { if (TDR_attach_database(status_vector, trans, reinterpret_cast<char*>(trans->tdr_fullpath->str_data))) { return; } } else if (trans->tdr_host_site) { // try going through the previous host with all available // protocols, using chaining to try the same method of // attachment originally used from that host char* p = buffer; const UCHAR* q = trans->tdr_host_site->str_data; while (*q && p < end) *p++ = *q++; *p++ = ':'; q = trans->tdr_fullpath->str_data; while (*q && p < end) *p++ = *q++; *p = 0; if (TDR_attach_database(status_vector, trans, buffer)) { return; } } // attaching using the old method didn't work; // try attaching to the remote node directly if (trans->tdr_remote_site) { char* p = buffer; const UCHAR* q = trans->tdr_remote_site->str_data; while (*q && p < end) *p++ = *q++; *p++ = ':'; q = reinterpret_cast<const UCHAR*>(trans->tdr_filename); while (*q && p < end) *p++ = *q++; *p = 0; if (TDR_attach_database (status_vector, trans, buffer)) { return; } } } // we have failed to reattach; notify the user // and let them try to succeed where we have failed ALICE_print(86, SafeArg() << trans->tdr_id); // msg 86: Could not reattach to database for transaction %ld. ALICE_print(87, SafeArg() << (trans->tdr_fullpath ? (char*)(trans->tdr_fullpath->str_data) : "is unknown")); // msg 87: Original path: %s if (tdgbl->uSvc->isService()) { ALICE_exit(FINI_ERROR, tdgbl); } for (;;) { ALICE_print(88); // msg 88: Enter a valid path: char* p = buffer; while (p < end && (*p = getchar()) != '\n' && !feof(stdin) && !ferror(stdin)) ++p; *p = 0; if (!buffer[0]) break; p = buffer; while (*p == ' ') ++p; if (TDR_attach_database(status_vector, trans, p)) { const size_t p_len = strlen(p); alice_str* string = FB_NEW_RPT(*tdgbl->getDefaultPool(), p_len + 1) alice_str; strcpy(reinterpret_cast<char*>(string->str_data), p); string->str_length = static_cast<USHORT>(p_len); trans->tdr_fullpath = string; trans->tdr_filename = (TEXT *) string->str_data; return; } ALICE_print(89); // msg 89: Attach unsuccessful. } }
USHORT TDR_analyze(const tdr* trans) { if (trans == NULL) return TRA_none; // if the tdr for the first transaction is missing, // we can assume it was committed USHORT advice = TRA_none; USHORT state = trans->tdr_state; if (state == TRA_none) state = TRA_commit; else if (state == TRA_unknown) advice = TRA_unknown; for (trans = trans->tdr_next; trans; trans = trans->tdr_next) { switch (trans->tdr_state) { // an explicitly committed transaction necessitates a check for the // perverse case of a rollback, otherwise a commit if at all possible case TRA_commit: if (state == TRA_rollback) { ALICE_print(105); // msg 105: Warning: Multidatabase transaction is in inconsistent state for recovery. ALICE_print(106, SafeArg() << trans->tdr_id); // msg 106: Transaction %ld was committed, but prior ones were rolled back. return 0; } else advice = TRA_commit; break; // a prepared transaction requires a commit if there are missing // records up to now, otherwise only do something if somebody else // already has case TRA_limbo: switch (state) { case TRA_none: case TRA_commit: advice = TRA_commit; break; case TRA_rollback: advice = TRA_rollback; break; } break; // an explicitly rolled back transaction requires a rollback unless a // transaction has committed or is assumed committed case TRA_rollback: if ((state == TRA_commit) || (state == TRA_none)) { ALICE_print(105); // msg 105: Warning: Multidatabase transaction is in inconsistent state for recovery. ALICE_print(107, SafeArg() << trans->tdr_id); // msg 107: Transaction %ld was rolled back, but prior ones were committed. return 0; } advice = TRA_rollback; break; // a missing TDR indicates a committed transaction if a limbo one hasn't // been found yet, otherwise it implies that the transaction wasn't // prepared case TRA_none: if (state == TRA_commit) advice = TRA_commit; else if (state == TRA_limbo) advice = TRA_rollback; break; // specifically advise TRA_unknown to prevent assumption that all are // in limbo case TRA_unknown: if (!advice) advice = TRA_unknown; break; default: ALICE_print(67, SafeArg() << trans->tdr_state); // msg 67: Transaction state %d not in valid range. return 0; } } return advice; }
static void print_description(const tdr* trans) { AliceGlobals* tdgbl = AliceGlobals::getSpecific(); if (!trans) { return; } if (!tdgbl->uSvc->isService()) { ALICE_print(92); // msg 92: Multidatabase transaction: } bool prepared_seen = false; for (const tdr* ptr = trans; ptr; ptr = ptr->tdr_next) { if (ptr->tdr_host_site) { const char* pszHostSize = reinterpret_cast<const char*>(ptr->tdr_host_site->str_data); if (!tdgbl->uSvc->isService()) { // msg 93: Host Site: %s ALICE_print(93, SafeArg() << pszHostSize); } tdgbl->uSvc->putLine(isc_spb_tra_host_site, pszHostSize); } if (ptr->tdr_id) { if (!tdgbl->uSvc->isService()) { // msg 94: Transaction %ld ALICE_print(94, SafeArg() << ptr->tdr_id); } tdgbl->uSvc->putSLong(isc_spb_tra_id, ptr->tdr_id); } switch (ptr->tdr_state) { case TRA_limbo: if (!tdgbl->uSvc->isService()) { ALICE_print(95); // msg 95: has been prepared. } tdgbl->uSvc->putChar(isc_spb_tra_state, isc_spb_tra_state_limbo); prepared_seen = true; break; case TRA_commit: if (!tdgbl->uSvc->isService()) { ALICE_print(96); // msg 96: has been committed. } tdgbl->uSvc->putChar(isc_spb_tra_state, isc_spb_tra_state_commit); break; case TRA_rollback: if (!tdgbl->uSvc->isService()) { ALICE_print(97); // msg 97: has been rolled back. } tdgbl->uSvc->putChar(isc_spb_tra_state, isc_spb_tra_state_rollback); break; case TRA_unknown: if (!tdgbl->uSvc->isService()) { ALICE_print(98); // msg 98: is not available. } tdgbl->uSvc->putChar(isc_spb_tra_state, isc_spb_tra_state_unknown); break; default: if (!tdgbl->uSvc->isService()) { // msg 99: is not found, assumed not prepared. // msg 100: is not found, assumed to be committed. ALICE_print(prepared_seen ? 99 : 100); } break; } if (ptr->tdr_remote_site) { const char* pszRemoteSite = reinterpret_cast<const char*>(ptr->tdr_remote_site->str_data); if (!tdgbl->uSvc->isService()) { // msg 101: Remote Site: %s ALICE_print(101, SafeArg() << pszRemoteSite); } tdgbl->uSvc->putLine(isc_spb_tra_remote_site, pszRemoteSite); } if (ptr->tdr_fullpath) { const char* pszFullpath = reinterpret_cast<const char*>(ptr->tdr_fullpath->str_data); if (!tdgbl->uSvc->isService()) { // msg 102: Database Path: %s ALICE_print(102, SafeArg() << pszFullpath); } tdgbl->uSvc->putLine(isc_spb_tra_db_path, pszFullpath); } } // let the user know what the suggested action is switch (TDR_analyze(trans)) { case TRA_commit: if (!tdgbl->uSvc->isService()) { // msg 103: Automated recovery would commit this transaction. ALICE_print(103); } tdgbl->uSvc->putChar(isc_spb_tra_advise, isc_spb_tra_advise_commit); break; case TRA_rollback: if (!tdgbl->uSvc->isService()) { // msg 104: Automated recovery would rollback this transaction. ALICE_print(104); } tdgbl->uSvc->putChar(isc_spb_tra_advise, isc_spb_tra_advise_rollback); break; default: tdgbl->uSvc->putChar(isc_spb_tra_advise, isc_spb_tra_advise_unknown); break; } }
bool TDR_reconnect_multiple(FB_API_HANDLE handle, SLONG id, const TEXT* name, SINT64 switches) { ISC_STATUS_ARRAY status_vector; // get the state of all the associated transactions tdr* trans = MET_get_transaction(status_vector, handle, id); if (!trans) return reconnect(handle, id, name, switches); reattach_databases(trans); TDR_get_states(trans); // analyze what to do with them; if the advice contradicts the user's // desire, make them confirm it; otherwise go with the flow. const USHORT advice = TDR_analyze(trans); if (!advice) { print_description(trans); switches = ask(); } else { switch (advice) { case TRA_rollback: if (switches & sw_commit) { ALICE_print(74, SafeArg() << trans->tdr_id); // msg 74: A commit of transaction %ld will violate two-phase commit. print_description(trans); switches = ask(); } else if (switches & sw_rollback) switches |= sw_rollback; else if (switches & sw_two_phase) switches |= sw_rollback; else if (switches & sw_prompt) { ALICE_print(75, SafeArg() << trans->tdr_id); // msg 75: A rollback of transaction %ld is needed to preserve two-phase commit. print_description(trans); switches = ask(); } break; case TRA_commit: if (switches & sw_rollback) { ALICE_print(76, SafeArg() << trans->tdr_id); // msg 76: Transaction %ld has already been partially committed. ALICE_print(77); // msg 77: A rollback of this transaction will violate two-phase commit. print_description(trans); switches = ask(); } else if (switches & sw_commit) switches |= sw_commit; else if (switches & sw_two_phase) switches |= sw_commit; else if (switches & sw_prompt) { ALICE_print(78, SafeArg() << trans->tdr_id); // msg 78: Transaction %ld has been partially committed. ALICE_print(79); // msg 79: A commit is necessary to preserve the two-phase commit. print_description(trans); switches = ask(); } break; case TRA_unknown: ALICE_print(80); // msg 80: Insufficient information is available to determine ALICE_print(81, SafeArg() << trans->tdr_id); // msg 81: a proper action for transaction %ld. print_description(trans); switches = ask(); break; default: if (!(switches & (sw_commit | sw_rollback))) { ALICE_print(82, SafeArg() << trans->tdr_id); // msg 82: Transaction %ld: All subtransactions have been prepared. ALICE_print(83); // msg 83: Either commit or rollback is possible. print_description(trans); switches = ask(); } } } bool error = false; if (switches != ULONG(~0)) { // now do the required operation with all the subtransactions if (switches & (sw_commit | sw_rollback)) { for (tdr* ptr = trans; ptr; ptr = ptr->tdr_next) { if (ptr->tdr_state == TRA_limbo) { reconnect(ptr->tdr_db_handle, ptr->tdr_id, ptr->tdr_filename, switches); } } } } else { ALICE_print(84); // msg 84: unexpected end of input error = true; } // shutdown all the databases for cleanliness' sake TDR_shutdown_databases(trans); return error; }
void TDR_list_limbo(FB_API_HANDLE handle, const TEXT* name, const SINT64 switches) { UCHAR buffer[1024]; ISC_STATUS_ARRAY status_vector; AliceGlobals* tdgbl = AliceGlobals::getSpecific(); if (isc_database_info(status_vector, &handle, sizeof(limbo_info), reinterpret_cast<const char*>(limbo_info), sizeof(buffer), reinterpret_cast<char*>(buffer))) { ALICE_print_status(true, status_vector); return; } SLONG id; tdr* trans; UCHAR* ptr = buffer; bool flag = true; while (flag) { const USHORT item = *ptr++; const USHORT length = (USHORT) gds__vax_integer(ptr, 2); ptr += 2; switch (item) { case isc_info_limbo: id = gds__vax_integer(ptr, length); if (switches & (sw_commit | sw_rollback | sw_two_phase | sw_prompt)) { TDR_reconnect_multiple(handle, id, name, switches); ptr += length; break; } if (!tdgbl->uSvc->isService()) { ALICE_print(71, SafeArg() << id); // msg 71: Transaction %d is in limbo. } if (trans = MET_get_transaction(status_vector, handle, id)) { tdgbl->uSvc->putSLong(isc_spb_multi_tra_id, id); reattach_databases(trans); TDR_get_states(trans); TDR_shutdown_databases(trans); print_description(trans); } else { tdgbl->uSvc->putSLong(isc_spb_single_tra_id, id); } ptr += length; break; case isc_info_truncated: if (!tdgbl->uSvc->isService()) { ALICE_print(72); // msg 72: More limbo transactions than fit. Try again // And how it's going to retry with a bigger buffer if the buffer is fixed size? } // fall through case isc_info_end: flag = false; break; default: if (!tdgbl->uSvc->isService()) { ALICE_print(73, SafeArg() << item); // msg 73: Unrecognized info item %d } } } }
int CLIB_ROUTINE main(int argc, char** argv) { /************************************** * * m a i n * ************************************** * * Functional description * Top level routine. * **************************************/ // Look at options, if any Firebird::PathName startup_file = STARTUP_FILE; #ifdef UNIX // If a Unix system, get home directory from environment SCHAR home_directory[MAXPATHLEN]; if (!fb_utils::readenv("HOME", startup_file)) startup_file = ".qli_startup"; else startup_file.append("/.qli_startup"); #endif #ifdef HAVE_LOCALE_H // Pick up the system locale to allow SYSTEM<->UTF8 conversions setlocale(LC_CTYPE, ""); #endif atexit(&atexit_fb_shutdown); const TEXT* application_file = NULL; ALLQ_init(); LEX_init(); bool version_flag = false; bool banner_flag = true; sw_buffers = 0; strcpy(QLI_prompt_string, "QLI> "); strcpy(QLI_cont_string, "CON> "); // Let's define the default number of columns on a machine by machine basis QLI_columns = 80; #ifdef TRUSTED_AUTH QLI_trusted = false; #endif QLI_nodb_triggers = false; QLI_lines = 60; QLI_name_columns = 0; QLI_prompt = QLI_prompt_string; QLI_matching_language = 0; QLI_default_user[0] = 0; QLI_default_password[0] = 0; QLI_charset[0] = 0; QLI_quit_flag = false; bool help_flag = false; #ifdef DEV_BUILD QLI_hex_output = false; #endif SLONG debug_value; // aparently unneeded, see usage below. const Switches switches(qli_in_sw_table, FB_NELEM(qli_in_sw_table), false, true); const TEXT* const* const arg_end = argv + argc; argv++; while (argv < arg_end) { const TEXT* const p = *argv++; if (*p != '-') { banner_flag = false; LEX_pop_line(); LEX_push_string(p); continue; } if (!p[1]) continue; const Switches::in_sw_tab_t* option = switches.findSwitch(p); const int in_sw = option ? option->in_sw : IN_SW_QLI_0; switch (in_sw) { case IN_SW_QLI_APP_SCRIPT: if (argv >= arg_end) { ERRQ_msg_put(23); // Msg23 Please retry, supplying an application script file name exit(FINI_ERROR); } application_file = *argv++; break; case IN_SW_QLI_BUFFERS: if (argv < arg_end && **argv != '-') sw_buffers = atoi(*argv++); break; case IN_SW_QLI_EXIT: QLI_quit_flag = true; break; case IN_SW_QLI_FETCH_PASSWORD: { if (argv >= arg_end || **argv == '-') break; const char* pwd = NULL; if (fb_utils::fetchPassword(*argv++, pwd) != fb_utils::FETCH_PASS_OK) break; fb_utils::copy_terminate(QLI_default_password, pwd, sizeof(QLI_default_password)); } break; case IN_SW_QLI_INITIAL_SCRIPT: if (argv >= arg_end || **argv == '-') startup_file = ""; else startup_file = *argv++; break; #ifdef TRUSTED_AUTH case IN_SW_QLI_TRUSTED_AUTH: QLI_trusted = true; break; #endif case IN_SW_QLI_NOBANNER: banner_flag = false; break; case IN_SW_QLI_NODBTRIGGERS: QLI_nodb_triggers = true; break; case IN_SW_QLI_PASSWORD: if (argv >= arg_end || **argv == '-') break; fb_utils::copy_terminate(QLI_default_password, fb_utils::get_passwd(*argv++), sizeof(QLI_default_password)); break; case IN_SW_QLI_TRACE: sw_trace = true; break; case IN_SW_QLI_USER: if (argv >= arg_end || **argv == '-') break; fb_utils::copy_terminate(QLI_default_user, *argv++, sizeof(QLI_default_user)); break; case IN_SW_QLI_VERIFY: sw_verify = true; break; case IN_SW_QLI_X: debug_value = 1; isc_set_debug(debug_value); break; // This switch's name is arbitrary; since it is an internal // mechanism it can be changed at will case IN_SW_QLI_Y: QLI_trace = true; break; case IN_SW_QLI_Z: version_flag = true; break; case IN_SW_QLI_HELP: help_flag = true; break; default: ERRQ_msg_put(529, SafeArg() << p); // Msg469 qli: ignoring unknown switch %c break; } } enable_signals(); if (help_flag) { usage(switches); HELP_fini(); MET_shutdown(); LEX_fini(); ALLQ_fini(); return FINI_OK; } if (banner_flag) ERRQ_msg_put(24); // Msg24 Welcome to QLI Query Language Interpreter if (version_flag) ERRQ_msg_put(25, SafeArg() << FB_VERSION); // Msg25 qli version %s if (application_file) LEX_push_file(application_file, true); else QLI_quit_flag = false; // Silently ignore -E switch when no script is given if (startup_file.length()) LEX_push_file(startup_file.c_str(), false); #if defined(_MSC_VER) && _MSC_VER >= 1400 _set_output_format(_TWO_DIGIT_EXPONENT); #endif for (bool got_started = false; !got_started;) { got_started = true; try { PAR_token(); } catch (const Firebird::Exception&) { // try again got_started = false; ERRQ_pending(); } } QLI_error = NULL; // Loop until end of file or forced exit bool flush_flag = false; while (QLI_line) { qli_plb* temp = QLI_default_pool = ALLQ_pool(); flush_flag = process_statement(flush_flag); ERRQ_pending(); ALLQ_rlpool(temp); } HELP_fini(); MET_shutdown(); LEX_fini(); ALLQ_fini(); #ifdef DEBUG_GDS_ALLOC // Report any memory leaks noticed. // We don't particularly care about QLI specific memory leaks, so all // QLI allocations have been marked as "don't report". However, much // of the test-base uses QLI so having a report when QLI finishes // could find leaks within the engine. gds_alloc_report(0 ALLOC_ARGS); #endif return (FINI_OK); }
static bool process_statement(bool flush_flag) { /************************************** * * p r o c e s s _ s t a t e m e n t * ************************************** * * Functional description * Parse, compile, and execute a single statement. If an input flush * is required, return true (or status), otherwise return false. * **************************************/ // Clear database active flags in preparation for a new statement QLI_abort = false; for (qli_dbb* dbb = QLI_databases; dbb; dbb = dbb->dbb_next) dbb->dbb_flags &= ~DBB_active; // If the last statement wrote out anything to the terminal, skip a line if (QLI_skip_line) { printf("\n"); QLI_skip_line = false; } // Enable signal handling for the next statement. Each signal will // be caught at least once, then reset to allow the user to really // kill the process enable_signals(); // Enable error unwinding and enable the unwinding environment try { // Set up the appropriate prompt and get the first significant token. If // we don't get one, we're at end of file QLI_prompt = QLI_prompt_string; // This needs to be done after setting QLI_prompt to prevent // and infinite loop in LEX/next_line. // If there was a prior syntax error, flush the token stream if (flush_flag) LEX_flush(); while (QLI_token->tok_keyword == KW_SEMI) LEX_token(); PAR_real(); if (!QLI_line) return false; EXEC_poll_abort(); // Mark the current token as starting the statement. This is allows // the EDIT command to find the last statement LEX_mark_statement(); // Change the prompt string to the continuation prompt, and parse // the next statement QLI_prompt = QLI_cont_string; qli_syntax* syntax_tree = PARQ_parse(); if (!syntax_tree) return false; EXEC_poll_abort(); // If the statement was EXIT, force end of file on command input if (syntax_tree->syn_type == nod_exit) { QLI_line = NULL; return false; } // If the statement was quit, ask the user if he want to rollback if (syntax_tree->syn_type == nod_quit) { QLI_line = NULL; for (qli_dbb* dbb = QLI_databases; dbb; dbb = dbb->dbb_next) { if ((dbb->dbb_transaction) && (dbb->dbb_flags & DBB_updates)) { // Msg460 Do you want to rollback updates for <dbb>? if (yes_no(460, dbb->dbb_symbol->sym_string)) MET_transaction(nod_rollback, dbb); else MET_transaction(nod_commit, dbb); } } return false; } // Expand the statement. It will return NULL if the statement was // a command. An error will be unwound qli_nod* expanded_tree = EXP_expand(syntax_tree); if (!expanded_tree) return false; // Compile the statement qli_nod* execution_tree = CMPQ_compile(expanded_tree); if (!execution_tree) return false; // Generate any BLR needed to support the request if (!GEN_generate(execution_tree)) return false; if (QLI_statistics) { for (qli_dbb* dbb = QLI_databases; dbb; dbb = dbb->dbb_next) { if (dbb->dbb_flags & DBB_active) { if (!dbb->dbb_statistics) { dbb->dbb_statistics = (int*) gds__alloc((SLONG) sizeof(PERF64)); #ifdef DEBUG_GDS_ALLOC // We don't care about QLI specific memory leaks for V4.0 gds_alloc_flag_unfreed((void *) dbb->dbb_statistics); // QLI: don't care #endif } perf64_get_info(&dbb->dbb_handle, (perf64*) dbb->dbb_statistics); } } } // Execute the request, for better or worse EXEC_top(execution_tree); if (QLI_statistics) { PERF64 statistics; TEXT buffer[512], report[256]; for (qli_dbb* dbb = QLI_databases; dbb; dbb = dbb->dbb_next) { report[0] = 0; if (dbb->dbb_flags & DBB_active) { ERRQ_msg_get(505, report, sizeof(report)); // Msg505 " reads = !r writes = !w fetches = !f marks = !m\n" FB_SIZE_T used_len = fb_strlen(report); ERRQ_msg_get(506, report + used_len, sizeof(report) - used_len); // Msg506 " elapsed = !e cpu = !u system = !s mem = !x, buffers = !b" perf64_get_info(&dbb->dbb_handle, &statistics); perf64_format((perf64*) dbb->dbb_statistics, &statistics, report, buffer, 0); ERRQ_msg_put(26, SafeArg() << dbb->dbb_filename << buffer); // Msg26 Statistics for database %s %s QLI_skip_line = true; } } } // Release resources associated with the request GEN_release(); return false; } // try catch (const Firebird::Exception&) { GEN_release(); return true; } }