X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/8c6fa3fbaccc22127280bf77a48fab5a3ee0716e..46b0747592074017ff0ea4b33d4a7194235886e5:/libs/ibpp/service.cpp diff --git a/libs/ibpp/service.cpp b/libs/ibpp/service.cpp new file mode 100644 index 00000000..26726ea1 --- /dev/null +++ b/libs/ibpp/service.cpp @@ -0,0 +1,774 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// File : $Id: service.cpp,v 1.1 2007/05/05 17:00:43 faust Exp $ +// Subject : IBPP, Service class implementation +// +/////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org) +// +// The contents of this file are subject to the IBPP License (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.ibpp.org or in the 'license.txt' +// file which must have been distributed along with this file. +// +// This 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. +// +/////////////////////////////////////////////////////////////////////////////// +// +// COMMENTS +// * Tabulations should be set every four characters when editing this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +#pragma warning(disable: 4786 4996) +#ifndef _DEBUG +#pragma warning(disable: 4702) +#endif +#endif + +#include "_ibpp.h" + +#ifdef HAS_HDRSTOP +#pragma hdrstop +#endif + +using namespace ibpp_internals; + +#ifdef IBPP_UNIX +#include +#define Sleep(x) usleep(x) +#endif + +// (((((((( OBJECT INTERFACE IMPLEMENTATION )))))))) + +void ServiceImpl::Connect() +{ + if (mHandle != 0) return; // Already connected + + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mUserName.empty()) + throw LogicExceptionImpl("Service::Connect", _("Unspecified user name.")); + if (mUserPassword.empty()) + throw LogicExceptionImpl("Service::Connect", _("Unspecified user password.")); + + // Attach to the Service Manager + IBS status; + SPB spb; + std::string connect; + + // Build a SPB based on the properties + spb.Insert(isc_spb_version); + spb.Insert(isc_spb_current_version); + spb.InsertString(isc_spb_user_name, 1, mUserName.c_str()); + spb.InsertString(isc_spb_password, 1, mUserPassword.c_str()); + + if (! mServerName.empty()) + { + connect = mServerName; + connect += ":"; + } + + connect += "service_mgr"; + + (*gds.Call()->m_service_attach)(status.Self(), (short)connect.size(), (char*)connect.c_str(), + &mHandle, spb.Size(), spb.Self()); + if (status.Errors()) + { + mHandle = 0; // Should be, but better be sure... + throw SQLExceptionImpl(status, "Service::Connect", _("isc_service_attach failed")); + } +} + +void ServiceImpl::Disconnect() +{ + if (mHandle == 0) return; // Already disconnected + + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + + IBS status; + + // Detach from the service manager + (*gds.Call()->m_service_detach)(status.Self(), &mHandle); + + // Set mHandle to 0 now, just in case we need to throw, because Disconnect() + // is called from Service destructor and we want to maintain a coherent state. + mHandle = 0; + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::Disconnect", _("isc_service_detach failed")); +} + +void ServiceImpl::GetVersion(std::string& version) +{ + // Based on a patch provided by Torsten Martinsen (SourceForge 'bullestock') + + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::GetVersion", _("Service is not connected.")); + + IBS status; + SPB spb; + RB result(250); + + spb.Insert(isc_info_svc_server_version); + + (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0, spb.Size(), spb.Self(), + result.Size(), result.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::GetVersion", _("isc_service_query failed")); + + result.GetString(isc_info_svc_server_version, version); +} + +void ServiceImpl::AddUser(const IBPP::User& user) +{ + if (gds.Call()->mGDSVersion >= 60 && mHandle == 0) + throw LogicExceptionImpl("Service::AddUser", _("Service is not connected.")); + if (user.username.empty()) + throw LogicExceptionImpl("Service::AddUser", _("Username required.")); + if (user.password.empty()) + throw LogicExceptionImpl("Service::AddUser", _("Password required.")); + + IBS status; + SPB spb; + spb.Insert(isc_action_svc_add_user); + spb.InsertString(isc_spb_sec_username, 2, user.username.c_str()); + spb.InsertString(isc_spb_sec_password, 2, user.password.c_str()); + if (! user.firstname.empty()) + spb.InsertString(isc_spb_sec_firstname, 2, user.firstname.c_str()); + if (! user.middlename.empty()) + spb.InsertString(isc_spb_sec_middlename, 2, user.middlename.c_str()); + if (! user.lastname.empty()) + spb.InsertString(isc_spb_sec_lastname, 2, user.lastname.c_str()); + if (user.userid != 0) + spb.InsertQuad(isc_spb_sec_userid, (int32_t)user.userid); + if (user.groupid != 0) + spb.InsertQuad(isc_spb_sec_groupid, (int32_t)user.groupid); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::AddUser", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::ModifyUser(const IBPP::User& user) +{ + if (gds.Call()->mGDSVersion >= 60 && mHandle == 0) + throw LogicExceptionImpl("Service::ModifyUser", _("Service is not connected.")); + if (user.username.empty()) + throw LogicExceptionImpl("Service::ModifyUser", _("Username required.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_modify_user); + spb.InsertString(isc_spb_sec_username, 2, user.username.c_str()); + if (! user.password.empty()) + spb.InsertString(isc_spb_sec_password, 2, user.password.c_str()); + if (! user.firstname.empty()) + spb.InsertString(isc_spb_sec_firstname, 2, user.firstname.c_str()); + if (! user.middlename.empty()) + spb.InsertString(isc_spb_sec_middlename, 2, user.middlename.c_str()); + if (! user.lastname.empty()) + spb.InsertString(isc_spb_sec_lastname, 2, user.lastname.c_str()); + if (user.userid != 0) + spb.InsertQuad(isc_spb_sec_userid, (int32_t)user.userid); + if (user.groupid != 0) + spb.InsertQuad(isc_spb_sec_groupid, (int32_t)user.groupid); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::ModifyUser", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::RemoveUser(const std::string& username) +{ + + if (gds.Call()->mGDSVersion >= 60 && mHandle == 0) + throw LogicExceptionImpl("Service::RemoveUser", _("Service is not connected.")); + if (username.empty()) + throw LogicExceptionImpl("Service::RemoveUser", _("Username required.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_delete_user); + spb.InsertString(isc_spb_sec_username, 2, username.c_str()); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::RemoveUser", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::GetUser(IBPP::User& user) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::GetUser", _("Service is not connected.")); + if (user.username.empty()) + throw LogicExceptionImpl("Service::GetUser", _("Username required.")); + + SPB spb; + spb.Insert(isc_action_svc_display_user); + spb.InsertString(isc_spb_sec_username, 2, user.username.c_str()); + + IBS status; + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::GetUser", _("isc_service_start failed")); + + RB result(8000); + char request[] = {isc_info_svc_get_users}; + status.Reset(); + (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0, + sizeof(request), request, result.Size(), result.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::GetUser", _("isc_service_query failed")); + + char* p = result.Self(); + if (*p != isc_info_svc_get_users) + throw SQLExceptionImpl(status, "Service::GetUser", _("isc_service_query returned unexpected answer")); + + p += 3; // Skips the 'isc_info_svc_get_users' and its total length + user.clear(); + while (*p != isc_info_end) + { + if (*p == isc_spb_sec_userid) + { + user.userid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4); + p += 5; + } + else if (*p == isc_spb_sec_groupid) + { + user.groupid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4); + p += 5; + } + else + { + unsigned short len = (unsigned short)(*gds.Call()->m_vax_integer)(p+1, 2); + switch (*p) + { + case isc_spb_sec_username : + // For each user, this is the first element returned + if (len != 0) user.username.assign(p+3, len); + break; + case isc_spb_sec_password : + if (len != 0) user.password.assign(p+3, len); + break; + case isc_spb_sec_firstname : + if (len != 0) user.firstname.assign(p+3, len); + break; + case isc_spb_sec_middlename : + if (len != 0) user.middlename.assign(p+3, len); + break; + case isc_spb_sec_lastname : + if (len != 0) user.lastname.assign(p+3, len); + break; + } + p += (3 + len); + } + } +} + +void ServiceImpl::GetUsers(std::vector& users) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::GetUsers", _("Service is not connected.")); + + SPB spb; + spb.Insert(isc_action_svc_display_user); + + IBS status; + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::GetUsers", _("isc_service_start failed")); + + RB result(8000); + char request[] = {isc_info_svc_get_users}; + status.Reset(); + (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0, + sizeof(request), request, result.Size(), result.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::GetUsers", _("isc_service_query failed")); + + users.clear(); + char* p = result.Self(); + if (*p != isc_info_svc_get_users) + throw SQLExceptionImpl(status, "Service::GetUsers", _("isc_service_query returned unexpected answer")); + + p += 3; // Skips the 'isc_info_svc_get_users' and its total length + IBPP::User user; + while (*p != isc_info_end) + { + if (*p == isc_spb_sec_userid) + { + user.userid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4); + p += 5; + } + else if (*p == isc_spb_sec_groupid) + { + user.groupid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4); + p += 5; + } + else + { + unsigned short len = (unsigned short)(*gds.Call()->m_vax_integer)(p+1, 2); + switch (*p) + { + case isc_spb_sec_username : + // For each user, this is the first element returned + if (! user.username.empty()) users.push_back(user); // Flush previous user + user.clear(); + if (len != 0) user.username.assign(p+3, len); + break; + case isc_spb_sec_password : + if (len != 0) user.password.assign(p+3, len); + break; + case isc_spb_sec_firstname : + if (len != 0) user.firstname.assign(p+3, len); + break; + case isc_spb_sec_middlename : + if (len != 0) user.middlename.assign(p+3, len); + break; + case isc_spb_sec_lastname : + if (len != 0) user.lastname.assign(p+3, len); + break; + } + p += (3 + len); + } + } + if (! user.username.empty()) users.push_back(user); // Flush last user +} + +void ServiceImpl::SetPageBuffers(const std::string& dbfile, int buffers) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::SetPageBuffers", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::SetPageBuffers", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_properties); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + spb.InsertQuad(isc_spb_prp_page_buffers, buffers); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::SetPageBuffers", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::SetSweepInterval(const std::string& dbfile, int sweep) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::SetSweepInterval", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::SetSweepInterval", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_properties); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + spb.InsertQuad(isc_spb_prp_sweep_interval, sweep); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::SetSweepInterval", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::SetSyncWrite(const std::string& dbfile, bool sync) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::SetSyncWrite", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::SetSyncWrite", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_properties); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + if (sync) spb.InsertByte(isc_spb_prp_write_mode, (char)isc_spb_prp_wm_sync); + else spb.InsertByte(isc_spb_prp_write_mode, (char)isc_spb_prp_wm_async); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::SetSyncWrite", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::SetReadOnly(const std::string& dbfile, bool readonly) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::SetReadOnly", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::SetReadOnly", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_properties); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + if (readonly) spb.InsertByte(isc_spb_prp_access_mode, (char)isc_spb_prp_am_readonly); + else spb.InsertByte(isc_spb_prp_access_mode, (char)isc_spb_prp_am_readwrite); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::SetReadOnly", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::SetReserveSpace(const std::string& dbfile, bool reserve) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::SetReserveSpace", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::SetReserveSpace", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_properties); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + if (reserve) spb.InsertByte(isc_spb_prp_reserve_space, (char)isc_spb_prp_res); + else spb.InsertByte(isc_spb_prp_reserve_space, (char)isc_spb_prp_res_use_full); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::SetReserveSpace", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::Shutdown(const std::string& dbfile, IBPP::DSM mode, int sectimeout) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::Shutdown", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::Shutdown", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_properties); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + switch (mode) + { + case IBPP::dsDenyAttach : + spb.InsertQuad(isc_spb_prp_deny_new_attachments, sectimeout); + break; + case IBPP::dsDenyTrans : + spb.InsertQuad(isc_spb_prp_deny_new_transactions, sectimeout); + break; + case IBPP::dsForce : + spb.InsertQuad(isc_spb_prp_shutdown_db, sectimeout); + break; + } + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::Shutdown", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::Restart(const std::string& dbfile) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::Restart", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::Restart", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_properties); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + spb.InsertQuad(isc_spb_options, isc_spb_prp_db_online); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::Restart", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::Sweep(const std::string& dbfile) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::Sweep", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::Sweep", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_repair); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + spb.InsertQuad(isc_spb_options, isc_spb_rpr_sweep_db); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::Sweep", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::Repair(const std::string& dbfile, IBPP::RPF flags) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::Repair", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::Repair", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_repair); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + + unsigned int mask; + if (flags & IBPP::rpValidateFull) mask = (isc_spb_rpr_full | isc_spb_rpr_validate_db); + else if (flags & IBPP::rpValidatePages) mask = isc_spb_rpr_validate_db; + else if (flags & IBPP::rpMendRecords) mask = isc_spb_rpr_mend_db; + else throw LogicExceptionImpl("Service::Repair", + _("One of rpMendRecords, rpValidatePages, rpValidateFull is required.")); + + if (flags & IBPP::rpReadOnly) mask |= isc_spb_rpr_check_db; + if (flags & IBPP::rpIgnoreChecksums) mask |= isc_spb_rpr_ignore_checksum; + if (flags & IBPP::rpKillShadows) mask |= isc_spb_rpr_kill_shadows; + + spb.InsertQuad(isc_spb_options, mask); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::Repair", _("isc_service_start failed")); + + Wait(); +} + +void ServiceImpl::StartBackup(const std::string& dbfile, + const std::string& bkfile, IBPP::BRF flags) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::Backup", _("Service is not connected.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::Backup", _("Main database file must be specified.")); + if (bkfile.empty()) + throw LogicExceptionImpl("Service::Backup", _("Backup file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_backup); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + spb.InsertString(isc_spb_bkp_file, 2, bkfile.c_str()); + if (flags & IBPP::brVerbose) spb.Insert(isc_spb_verbose); + + unsigned int mask = 0; + if (flags & IBPP::brIgnoreChecksums) mask |= isc_spb_bkp_ignore_checksums; + if (flags & IBPP::brIgnoreLimbo) mask |= isc_spb_bkp_ignore_limbo; + if (flags & IBPP::brMetadataOnly) mask |= isc_spb_bkp_metadata_only; + if (flags & IBPP::brNoGarbageCollect) mask |= isc_spb_bkp_no_garbage_collect; + if (flags & IBPP::brNonTransportable) mask |= isc_spb_bkp_non_transportable; + if (flags & IBPP::brConvertExtTables) mask |= isc_spb_bkp_convert; + if (mask != 0) spb.InsertQuad(isc_spb_options, mask); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::Backup", _("isc_service_start failed")); +} + +void ServiceImpl::StartRestore(const std::string& bkfile, const std::string& dbfile, + int pagesize, IBPP::BRF flags) +{ + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + if (mHandle == 0) + throw LogicExceptionImpl("Service::Restore", _("Service is not connected.")); + if (bkfile.empty()) + throw LogicExceptionImpl("Service::Restore", _("Backup file must be specified.")); + if (dbfile.empty()) + throw LogicExceptionImpl("Service::Restore", _("Main database file must be specified.")); + + IBS status; + SPB spb; + + spb.Insert(isc_action_svc_restore); + spb.InsertString(isc_spb_bkp_file, 2, bkfile.c_str()); + spb.InsertString(isc_spb_dbname, 2, dbfile.c_str()); + if (flags & IBPP::brVerbose) spb.Insert(isc_spb_verbose); + if (pagesize != 0) spb.InsertQuad(isc_spb_res_page_size, pagesize); + + unsigned int mask; + if (flags & IBPP::brReplace) mask = isc_spb_res_replace; + else mask = isc_spb_res_create; // Safe default mode + + if (flags & IBPP::brDeactivateIdx) mask |= isc_spb_res_deactivate_idx; + if (flags & IBPP::brNoShadow) mask |= isc_spb_res_no_shadow; + if (flags & IBPP::brNoValidity) mask |= isc_spb_res_no_validity; + if (flags & IBPP::brPerTableCommit) mask |= isc_spb_res_one_at_a_time; + if (flags & IBPP::brUseAllSpace) mask |= isc_spb_res_use_all_space; + if (mask != 0) spb.InsertQuad(isc_spb_options, mask); + + (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "Service::Restore", _("isc_service_start failed")); +} + +const char* ServiceImpl::WaitMsg() +{ + IBS status; + SPB req; + RB result(1024); + + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + + req.Insert(isc_info_svc_line); // Request one line of textual output + + // _service_query will only block until a line of result is available + // (or until the end of the task if it does not report information) + (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0, + req.Size(), req.Self(), result.Size(), result.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "ServiceImpl::Wait", _("isc_service_query failed")); + + // If message length is zero bytes, task is finished + if (result.GetString(isc_info_svc_line, mWaitMessage) == 0) return 0; + + // Task is not finished, but we have something to report + return mWaitMessage.c_str(); +} + +void ServiceImpl::Wait() +{ + IBS status; + SPB spb; + RB result(1024); + std::string msg; + + if (gds.Call()->mGDSVersion < 60) + throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL")); + + spb.Insert(isc_info_svc_line); + for (;;) + { + // Sleeps 1 millisecond upfront. This will release the remaining + // timeslot of the thread. Doing so will give a good chance for small + // services tasks to finish before we check if they are still running. + // The deal is to limit (in that particular case) the number of loops + // polling _service_query that will happen. + + Sleep(1); + + // _service_query will only block until a line of result is available + // (or until the end of the task if it does not report information) + (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0, + spb.Size(), spb.Self(), result.Size(), result.Self()); + if (status.Errors()) + throw SQLExceptionImpl(status, "ServiceImpl::Wait", _("isc_service_query failed")); + + // If message length is zero bytes, task is finished + if (result.GetString(isc_info_svc_line, msg) == 0) return; + + status.Reset(); + result.Reset(); + } +} + +IBPP::IService* ServiceImpl::AddRef() +{ + ASSERTION(mRefCount >= 0); + ++mRefCount; + return this; +} + +void ServiceImpl::Release() +{ + // Release cannot throw, except in DEBUG builds on assertion + ASSERTION(mRefCount >= 0); + --mRefCount; + try { if (mRefCount <= 0) delete this; } + catch (...) { } +} + +// (((((((( OBJECT INTERNAL METHODS )))))))) + +void ServiceImpl::SetServerName(const char* newName) +{ + if (newName == 0) mServerName.erase(); + else mServerName = newName; +} + +void ServiceImpl::SetUserName(const char* newName) +{ + if (newName == 0) mUserName.erase(); + else mUserName = newName; +} + +void ServiceImpl::SetUserPassword(const char* newPassword) +{ + if (newPassword == 0) mUserPassword.erase(); + else mUserPassword = newPassword; +} + +ServiceImpl::ServiceImpl(const std::string& ServerName, + const std::string& UserName, const std::string& UserPassword) + : mRefCount(0), mHandle(0), + mServerName(ServerName), mUserName(UserName), mUserPassword(UserPassword) +{ +} + +ServiceImpl::~ServiceImpl() +{ + try { if (Connected()) Disconnect(); } + catch (...) { } +} + +// +// Eof +//