/*- * Copyright 2005-2007 Guram Dukashvili * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ //--------------------------------------------------------------------------- #include //--------------------------------------------------------------------------- namespace fbcpp { //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- DPB::DPBParam DPB::params[] = { { "version1", isc_dpb_version1 }, { "pathname", isc_dpb_cdd_pathname }, { "allocation", isc_dpb_allocation }, { "journal", isc_dpb_journal }, { "page_size", isc_dpb_page_size }, { "num_buffers", isc_dpb_num_buffers }, { "buffer_length", isc_dpb_buffer_length }, { "debug", isc_dpb_debug }, { "garbage_collect", isc_dpb_garbage_collect }, { "verify", isc_dpb_verify }, { "sweep", isc_dpb_sweep }, { "enable_journal", isc_dpb_enable_journal }, { "disable_journal", isc_dpb_disable_journal }, { "dbkey_scope", isc_dpb_dbkey_scope }, { "number_of_users", isc_dpb_number_of_users }, { "trace", isc_dpb_trace }, { "no_garbage_collect", isc_dpb_no_garbage_collect }, { "damaged", isc_dpb_damaged }, { "license", isc_dpb_license }, { "sys_user_name", isc_dpb_sys_user_name }, { "encrypt_key", isc_dpb_encrypt_key }, { "activate_shadow", isc_dpb_activate_shadow }, { "sweep_interval", isc_dpb_sweep_interval }, { "delete_shadow", isc_dpb_delete_shadow }, { "force_write", isc_dpb_force_write }, { "begin_log", isc_dpb_begin_log }, { "quit_log", isc_dpb_quit_log }, { "no_reserve", isc_dpb_no_reserve }, { "user_name", isc_dpb_user_name }, { "password", isc_dpb_password }, { "password_enc", isc_dpb_password_enc }, { "sys_user_name_enc", isc_dpb_sys_user_name_enc }, { "interp", isc_dpb_interp }, { "online_dump", isc_dpb_online_dump }, { "old_file_size", isc_dpb_old_file_size }, { "old_num_files", isc_dpb_old_num_files }, { "old_file", isc_dpb_old_file }, { "old_start_page", isc_dpb_old_start_page }, { "old_start_seqno", isc_dpb_old_start_seqno }, { "old_start_file", isc_dpb_old_start_file }, { "drop_walfile", isc_dpb_drop_walfile }, { "old_dump_id", isc_dpb_old_dump_id }, { "wal_backup_dir", isc_dpb_wal_backup_dir }, { "wal_chkptlen", isc_dpb_wal_chkptlen }, { "wal_numbufs", isc_dpb_wal_numbufs }, { "wal_bufsize", isc_dpb_wal_bufsize }, { "wal_grp_cmt_wait", isc_dpb_wal_grp_cmt_wait }, { "lc_messages", isc_dpb_lc_messages }, { "lc_ctype", isc_dpb_lc_ctype }, { "cache_manager", isc_dpb_cache_manager }, { "shutdown", isc_dpb_shutdown }, { "online", isc_dpb_online }, { "shutdown_delay", isc_dpb_shutdown_delay }, { "reserved", isc_dpb_reserved }, { "overwrite", isc_dpb_overwrite }, { "sec_attach", isc_dpb_sec_attach }, { "disable_wal", isc_dpb_disable_wal }, { "connect_timeout", isc_dpb_connect_timeout }, { "dummy_packet_interval", isc_dpb_dummy_packet_interval }, { "gbak_attach", isc_dpb_gbak_attach }, { "sql_role_name", isc_dpb_sql_role_name }, { "set_page_buffers", isc_dpb_set_page_buffers }, { "working_directory", isc_dpb_working_directory }, { "sql_dialect", isc_dpb_sql_dialect }, { "set_db_readonly", isc_dpb_set_db_readonly }, { "set_db_sql_dialect", isc_dpb_set_db_sql_dialect }, { "gfix_attach", isc_dpb_gfix_attach }, { "gstat_attach", isc_dpb_gstat_attach }, { "set_db_charset", isc_dpb_set_db_charset }, { "file_length", 127 } }; //--------------------------------------------------------------------------- DPB::DPB() : dpb_(NULL) { clear(); } //--------------------------------------------------------------------------- DPB::~DPB() { ksys::xfree(dpb_); } //--------------------------------------------------------------------------- DPB & DPB::clear() { ksys::xfree(dpb_); dpb_ = NULL; dpbLen_ = 0; dialect_ = 3; pageSize_ = 0; fileLength_ = 0; user_.resize(0); password_.resize(0); role_.resize(0); charset_ = "UNICODE_FSS"; charsetInDPB_ = false; timeout_ = ~(uint32_t(1) << (sizeof(uint32_t) * 8 - 1)); timeoutInDPB_ = false; writeChar(isc_dpb_version1); return *this; } //--------------------------------------------------------------------------- DPB & DPB::injectCharset() { if( !charsetInDPB_ ){ static const char charset[] = "UNICODE_FSS"; writeISCCode("lc_ctype"); writeChar(sizeof(charset) - 1). writeBuffer(charset, sizeof(charset) - 1).charsetInDPB_ = true; } return *this; } //--------------------------------------------------------------------------- DPB & DPB::injectTimeout() { if( !timeoutInDPB_ ){ add("connect_timeout",timeout_); timeoutInDPB_ = true; } return *this; } //--------------------------------------------------------------------------- DPB & DPB::writeChar(uintptr_t code) { ksys::xrealloc(dpb_, dpbLen_ + 1); dpb_[dpbLen_++] = char(code); return *this; } //--------------------------------------------------------------------------- DPB & DPB::writeLong(uintptr_t a) { ksys::xrealloc(dpb_, dpbLen_ + sizeof(int)); *(int *) (dpb_ + dpbLen_) = (int32_t) a; dpbLen_ += sizeof(int32_t); return *this; } //--------------------------------------------------------------------------- intptr_t DPB::writeISCCode(const utf8::String & name) { intptr_t i; for( i = sizeof(params) / sizeof(params[0]) - 1; i >= 0; i-- ){ if( name.strcasecmp(params[i].name_) == 0 ){ ksys::xrealloc(dpb_, dpbLen_ + 1); dpb_[dpbLen_++] = params[i].number; return params[i].number; } } return i; } //--------------------------------------------------------------------------- DPB & DPB::writeBuffer(const void * buf, uintptr_t size) { ksys::xrealloc(dpb_, dpbLen_ + size); memcpy(dpb_ + dpbLen_, buf, size); dpbLen_ += size; return *this; } //--------------------------------------------------------------------------- DPB & DPB::add(const utf8::String & name, const ksys::Mutant & value) { uintptr_t sweepInterval; utf8::String sValue; switch( writeISCCode(name) ){ case isc_dpb_sql_dialect : dialect_ = value; dpbLen_--; break; case isc_dpb_page_size : pageSize_ = value; dpbLen_--; break; case 127 : fileLength_ = value; dpbLen_--; break; case isc_dpb_user_name : user_ = value; goto l1; case isc_dpb_password : password_ = value; goto l1; case isc_dpb_password_enc : case isc_dpb_sys_user_name : case isc_dpb_license : case isc_dpb_encrypt_key : case isc_dpb_lc_messages : goto l1; case isc_dpb_lc_ctype : charset_ = value; dpbLen_--; break; case isc_dpb_sql_role_name : role_ = value; if( role_.strlen() == 0 ){ dpbLen_--; break; } l1: sValue = value; writeChar(sValue.size()).writeBuffer(sValue.c_str(),sValue.size()); break; case isc_dpb_num_buffers : case isc_dpb_dbkey_scope : case isc_dpb_force_write : case isc_dpb_no_reserve : case isc_dpb_damaged : case isc_dpb_verify : writeChar(1).writeChar(value); break; case isc_dpb_sweep : writeChar(1).writeChar(isc_dpb_records); break; case isc_dpb_sweep_interval : sweepInterval = (uintptr_t) value; api.open(); sweepInterval = api.isc_vax_integer((char *) &sweepInterval, 4); api.close(); writeChar(1).writeChar(sizeof(int32_t)).writeLong(sweepInterval); break; case isc_dpb_activate_shadow : case isc_dpb_delete_shadow : case isc_dpb_begin_log : case isc_dpb_quit_log : writeChar(1).writeChar(0); break; case isc_dpb_connect_timeout : case isc_dpb_dummy_packet_interval : writeChar(4).writeLong(timeout_); break; } return *this; } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- Database::Database() : handle_(0) { transactions_.ownsObjects(false); dsqlStatements_.ownsObjects(false); eventHandlers_.ownsObjects(false); } //--------------------------------------------------------------------------- Database::~Database() { detachHelper(); detach(); } //--------------------------------------------------------------------------- bool Database::separateDBName(const utf8::String & name, utf8::String & hostName, utf8::String & dbName, uintptr_t & port) { utf8::String::Iterator i1(name); while( !i1.eos() && i1.getChar() != ':' ) i1.next(); utf8::String::Iterator i2(i1 + 1); while( !i2.eos() && (i2.getChar() != ':' || (i2 + 1).getChar() == '\\') ) i2.next(); port = 0; if( i1.eos() && i2.eos() ){ dbName = name; } else{ intmax_t prt = port; if( utf8::tryStr2Int(utf8::String(i1 + 1, i2), prt) && i2.eos() ){ // dbName and port dbName = utf8::String(utf8::String::Iterator(name), i1); } else{ utf8::tryStr2Int(utf8::String(i2 + 1, utf8::String::Iterator(name).last()), prt); hostName = utf8::String(utf8::String::Iterator(name), i1); dbName = utf8::String(i1 + 1, i2); } port = (uintptr_t) prt; } return true; } //--------------------------------------------------------------------------- Database & Database::create(const utf8::String & name) { utf8::String createSQL("CREATE DATABASE '" + (name.strlen() > 0 ? name : name_) + "' "); if( dpb_.user().strlen() == 0 ){ createSQL += "USER 'SYSDBA' "; } else { createSQL += "USER '" + dpb_.user() + "' "; } if( dpb_.password().strlen() == 0 ){ createSQL += "PASSWORD 'masterkey' "; } else { createSQL += "PASSWORD '" + dpb_.password() + "' "; } if( dpb_.pageSize() <= 0 ){ createSQL += "PAGE_SIZE=1024 "; } else { createSQL += "PAGE_SIZE=" + utf8::int2Str((intmax_t) dpb_.pageSize()) + " "; } if( dpb_.fileLength() > 0 ){ createSQL += "LENGTH=" + utf8::int2Str(dpb_.fileLength() / dpb_.pageSize()) + " "; } createSQL += "DEFAULT CHARACTER SET " + dpb_.charset_; api.open(); isc_db_handle newdb = 0; isc_tr_handle trans = 0; ISC_STATUS_ARRAY status; if( api.isc_dsql_execute_immediate(status,&newdb,&trans,0,(char *) createSQL.c_str(),(short) dpb_.dialect(), NULL) != 0 ){ ksys::AutoPtr e(newObjectV1C2(status,__PRETTY_FUNCTION__)); if( !e->searchCode(isc_db_or_file_exists) ){ api.close(); e.ptr(NULL)->throwSP(); } } else { api.isc_detach_database(status,&newdb); } api.close(); return *this; } //--------------------------------------------------------------------------- Database & Database::detachHelper() { while( eventHandlers_.count() > 0 ){ eventHandlers_.objectOfIndex(0)->cancel(); eventHandlers_.removeByIndex(0); } while( dsqlStatements_.count() > 0 ){ DSQLStatement * dsqlStatement = dsqlStatements_.objectOfIndex(0); dsqlStatement->free(); dsqlStatement->database_ = NULL; dsqlStatements_.removeByIndex(0); } while( transactions_.count() > 0 ){ Transaction * transaction = transactions_.objectOfIndex(0); while( transaction->active() ) transaction->rollback(); #if __GNUG__ #else transaction->databases_.removeByObject(this); #endif transactions_.removeByIndex(0); } return *this; } //--------------------------------------------------------------------------- Database & Database::drop() { if( !attached() ) exceptionHandler(newObjectV1C2((ISC_STATUS *) NULL,__PRETTY_FUNCTION__)); ISC_STATUS_ARRAY status; if( api.isc_drop_database(status,&handle_) != 0 ) exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); detach(); return *this; } //--------------------------------------------------------------------------- Database & Database::attach(const utf8::String & name) { if( !attached() ){ dpb_.injectCharset().injectTimeout(); api.open(); ISC_STATUS_ARRAY status; if( api.isc_attach_database(status, 0, (char *) (name.strlen() > 0 ? name.c_str() : name_.c_str()), &handle_, (short) dpb_.dpbLen(), dpb_.dpb()) != 0 ){ api.close(); newObjectV1C2(status, __PRETTY_FUNCTION__)->throwSP(); } if( name.strlen() > 0 ) name_ = name; } return *this; } //--------------------------------------------------------------------------- Database & Database::detach() { intptr_t i; for( i = eventHandlers_.count() - 1; i >= 0; i-- ) eventHandlers_.objectOfIndex(i)->cancel(); for( i = dsqlStatements_.count() - 1; i >= 0; i-- ) dsqlStatements_.objectOfIndex(i)->free(); for( i = transactions_.count() - 1; i >= 0; i-- ){ Transaction * transaction = transactions_.objectOfIndex(0); while( transaction->active() ) transaction->rollback(); } if( attached() ){ ISC_STATUS_ARRAY status; api.isc_detach_database(status,&handle_); // if( api.isc_detach_database(status, &handle_) != 0 ) // exceptionHandler(newObjectV1C2(status, __PRETTY_FUNCTION__)); api.close(); } return *this; } //--------------------------------------------------------------------------- void Database::processingException(ksys::Exception * e) { // handle fatal errors EClientServer * p = dynamic_cast(e); if( p != NULL ){ if( p->isFatalError() ) detach(); } } //--------------------------------------------------------------------------- void Database::staticExceptionHandler(ksys::Exception * e) { for( intptr_t i = transactions_.count() - 1; i >= 0; i-- ) transactions_[i]->processingException(e); processingException(e); e->throwSP(); } //--------------------------------------------------------------------------- void Database::exceptionHandler(ksys::Exception * e) { staticExceptionHandler(e); } //--------------------------------------------------------------------------- Database & Database::name(const utf8::String & name) { detach(); name_ = name; return *this; } //--------------------------------------------------------------------------- } // namespace fbcpp //---------------------------------------------------------------------------