module firebird_database; private import ibase; private import firebirdCommon; private import firebird_databasebuffer; private import firebird_status; private import firebird_resultbuffer; private import std.string; private import std.c.string; private import std.c.stdlib; private import std.stdio; class Database { private: PARAMVARY VARY2; STATUS status; string mServerName; // Server name string mDatabaseName; // Database name(path/file) string mUserName; // User name string mUserPassword; // User password string mRoleName; // Role used for the duration of the connection string mCharSet; // Character Set used for the connection string mCreateParams; // Other parameters(creation only) int mDialect; // 1 if IB5, 1 or 3 if IB6/FB1 bool connected = false; isc_db_handle dbhandle = null; public: bool Connect(string dbname, string username, string password, string RoleName = "", string CharSet = "") { auto dpb = new DBPARAMBLOCK(); dpb.DPBInsert(isc_dpb_user_name, cast(char *) username.ptr); dpb.DPBInsert(isc_dpb_password, cast(char *) password.ptr); if(RoleName.length != 0) dpb.DPBInsert(isc_dpb_sql_role_name, cast(char *) RoleName.ptr); if(CharSet.length != 0) dpb.DPBInsert(isc_dpb_lc_ctype, cast(char *) CharSet.ptr); ISC_STATUS cresult = isc_attach_database(status.StatusHandle(),cast(short) dbname.length, cast(char *) dbname.ptr, &dbhandle, dpb.DPBSize(), dpb.DPBHandle()); if(status.StatusErrors()) { isc_detach_database(status.StatusHandle(), &dbhandle); return false; } char[3] basicInfo; basicInfo[0] = cast(char)(db_info_types.isc_info_ods_version); basicInfo[1] = cast(char)(db_info_types.isc_info_db_sql_dialect); basicInfo[2] = cast(char)(isc_info_end); status.StatusReset(); auto result = new RESULTBUFFER(256); cresult = isc_database_info(status.StatusHandle(), &dbhandle, cast(short) basicInfo.length, cast(char *) basicInfo.ptr, result.ResultbuffSize(), result.ResultbufferHandle()); if(status.StatusErrors()) { return false; } int ODS = result.ResultbufferGetValue(db_info_types.isc_info_ods_version); if(ODS <= 9) { Close(); return false; } mDialect = result.ResultbufferGetValue(isc_info_db_SQL_dialect); if(mDialect != 1 && mDialect != 3) { isc_detach_database(status.StatusHandle(), &dbhandle); dbhandle = null; // Should be, but better be sure... throw new Exception("Database::Connect Dialect 1 or 3 required"); } mServerName = ServerName.idup; mDatabaseName = dbname.idup; mUserName = username.idup; mUserPassword = password.idup; mRoleName = RoleName.idup; mCharSet = CharSet.idup; connected = true; return true; } ~this () { Close(); delete status; } this() { status = new STATUS; } this(string dbname, string username, string password, string RoleName = "", string CharSet = "") { connected = Connect(dbname, username, password, RoleName, CharSet); status = new STATUS; } isc_db_handle *GetDbHandle() { return &dbhandle; } bool isConnected() {return connected;} void Close () {/* Close the current connection to the database. */ status.StatusReset(); isc_detach_database(status.StatusHandle, &dbhandle); } int getstatuscode() { status.StatusReset(); int stat = status.StatusSqlCode(); return stat; } string getstatustext() { status.StatusReset(); string stat = status.toString(); return cast(string) stat.dup; } void Info(ref int ODSMajor, ref int ODSMinor, ref int PageSize, ref int Pages, ref int Buffers, ref int Sweep, ref bool Sync, ref bool Reserve) { if(dbhandle is null) throw new Exception("Database::Info Database is not connected."); char items[9] = [ cast(char) db_info_types.isc_info_ods_version, cast(char) db_info_types.isc_info_ods_minor_version, cast(char) db_info_types.isc_info_page_size, cast(char) db_info_types.isc_info_allocation, cast(char) db_info_types.isc_info_num_buffers, cast(char) db_info_types.isc_info_sweep_interval, cast(char) db_info_types.isc_info_forced_writes, cast(char) db_info_types.isc_info_no_reserve, cast(char) isc_info_end ]; auto result = new RESULTBUFFER(256); status.StatusReset(); isc_database_info(status.StatusHandle, &dbhandle, cast(short) items.length, cast(char *)items, result.ResultbuffSize, result.ResultbufferHandle); if(status.StatusErrors()) throw new Exception("Database::Info status isc_database_info failed"); if(ODSMajor != 0) ODSMajor = result.ResultbufferGetValue(db_info_types.isc_info_ods_version); if(ODSMinor != 0) ODSMinor = result.ResultbufferGetValue(db_info_types.isc_info_ods_minor_version); if(PageSize != 0) PageSize = result.ResultbufferGetValue(db_info_types.isc_info_page_size); if(Pages != 0) Pages = result.ResultbufferGetValue(db_info_types.isc_info_allocation); if(Buffers != 0) Buffers = result.ResultbufferGetValue(db_info_types.isc_info_num_buffers); if(Sweep != 0) Sweep = result.ResultbufferGetValue(db_info_types.isc_info_sweep_interval); if(Sync != 0) Sync = result.ResultbufferGetValue(db_info_types.isc_info_forced_writes) == 1 ? true : false; if(Reserve != 0) Reserve = result.ResultbufferGetValue(db_info_types.isc_info_no_reserve) == 1 ? false : true; } void Statistics(ref int Fetches, ref int Marks, ref int Reads, ref int Writes) { if(dbhandle is null) throw new Exception("Database::Statistics Database is not connected."); char items[] = [cast(char) db_info_types.isc_info_fetches, cast(char) db_info_types.isc_info_marks, cast(char) db_info_types.isc_info_reads, cast(char) db_info_types.isc_info_writes, cast(char) isc_info_end]; auto result = new RESULTBUFFER(128); status.StatusReset(); isc_database_info(status.StatusHandle, &dbhandle, cast(short) items.length, cast(char*) items, result.ResultbuffSize, result.ResultbufferHandle); if(status.StatusErrors()) throw new Exception("Database::Statistics status isc_database_info failed"); if(Fetches != 0) Fetches = result.ResultbufferGetValue(db_info_types.isc_info_fetches); if(Marks != 0) Marks = result.ResultbufferGetValue(db_info_types.isc_info_marks); if(Reads != 0) Reads = result.ResultbufferGetValue(db_info_types.isc_info_reads); if(Writes != 0) Writes = result.ResultbufferGetValue(db_info_types.isc_info_writes); delete result; } void Counts(ref int Insert, ref int Update, ref int Delete, ref int ReadIdx, ref int ReadSeq) { if(dbhandle is null) throw new Exception("Database::Counts Database is not connected."); char items[] = [cast(char) db_info_types.isc_info_insert_count, cast(char) db_info_types.isc_info_update_count, cast(char) db_info_types.isc_info_delete_count, cast(char) db_info_types.isc_info_read_idx_count, cast(char) db_info_types.isc_info_read_seq_count, cast(char) isc_info_end]; auto result = new RESULTBUFFER(1024); status.StatusReset(); isc_database_info(status.StatusHandle, &dbhandle, cast(short) items.length, cast(char *)items, result.ResultbuffSize, result.ResultbufferHandle); if(status.StatusErrors()) throw new Exception("Database::Counts status isc_database_info failed"); if(Insert == 0) Insert = result.ResultbufferGetCountValue(db_info_types.isc_info_insert_count); if(Update == 0) Update = result.ResultbufferGetCountValue(db_info_types.isc_info_update_count); if(Delete == 0) Delete = result.ResultbufferGetCountValue(db_info_types.isc_info_delete_count); if(ReadIdx == 0) ReadIdx = result.ResultbufferGetCountValue(db_info_types.isc_info_read_idx_count); if(ReadSeq == 0) ReadSeq = result.ResultbufferGetCountValue(db_info_types.isc_info_read_seq_count); delete result; } void Users(ref char[][] users) { if(dbhandle == null) throw new Exception("Database::Users Database is not connected."); char items[] = [cast(char) db_info_types.isc_info_user_names, cast(char) isc_info_end]; auto result = new RESULTBUFFER(8000); status.StatusReset(); isc_database_info(status.StatusHandle, &dbhandle, cast(short) items.length, cast(char *) items, result.ResultbuffSize, result.ResultbufferHandle); if(status.StatusErrors()) { delete result; throw new Exception("Database::Users status isc_database_info failed"); } users.length = 0; char* p = result.ResultbufferHandle; while(*p == db_info_types.isc_info_user_names) { p += 3; // Get to the length byte(there are two undocumented bytes which we skip) int len = cast(int)(*p); ++p; // Get to the first char of username if(len != 0) //users.push_back(char[]().append(p, len)); users ~= cast(char[]) p[0..len].dup; p += len; // Skip username } delete result; return; } int Dialect() { return mDialect; } string ServerName() { return mServerName; } string DatabaseName() { return mDatabaseName; } string Username() { return mUserName; } string UserPassword() { return mUserPassword; } string RoleName() { return mRoleName; } string CharSet() { return mCharSet; } string CreateParams() { return mCreateParams; } void Create(int dialect){ if(dbhandle !is null) throw new Exception("Database::Create Database is already connected."); if(mDatabaseName.length == 0) throw new Exception("Database::Create Unspecified database name."); if(mUserName.length == 0) throw new Exception("Database::Create Unspecified user name."); if(dialect != 1 && dialect != 3) throw new Exception("Database::Create Only dialects 1 and 3 are supported."); // Build the SQL Create Statement string create; create = "CREATE DATABASE '"; if(! mServerName.length == 0) create ~= mServerName ~ ":"; create ~= mDatabaseName ~ "' "; create ~= "USER '" ~ mUserName ~ "' "; if(! mUserPassword.length == 0) create ~= "PASSWORD '" ~ mUserPassword ~ "' "; if(! mCreateParams.length == 0) create ~= mCreateParams; // Call ExecuteImmediate to create the database isc_tr_handle tr_handle = null; status.StatusReset(); isc_dsql_execute_immediate(status.StatusHandle, &dbhandle, &tr_handle, 0, cast(char*)(create), cast(ushort)(dialect), null); if(status.StatusErrors()) { throw new Exception("Database::Create status isc_dsql_execute_immediate failed"); } Disconnect(); } bool Connected() { return dbhandle is null ? false : true; } void Disconnect() { if(dbhandle is null) return; // Not connected anyway // Detach from the server status.StatusReset(); isc_detach_database(status.StatusHandle, &dbhandle); // Should we throw, set mHandle to 0 first, because Disconnect() may be called from Database destructor(keeps the object coherent). dbhandle = null; if(status.StatusErrors()) { throw new Exception("Database::Disconnect status isc_detach_database failed"); } } void Drop() { if(dbhandle == null) throw new Exception("Database::Drop Database must be connected."); // Put the connection to a rest status.StatusReset(); isc_drop_database(status.StatusHandle, &dbhandle); if(status.StatusErrors()) { throw new Exception("Database::Drop vector isc_drop_database failed"); } dbhandle = null; } }