====== Minimalist Sample Code ====== This section of the documentation drives you, without explaining all the details, through short program excerpts. The purpose is to show you the big picture : how you can create or connect to a database and start working with it. ====== The Basics ====== ===== Compiling ===== All your project files which use IBPP obviously need to include the library header file. #include "ibpp.h" You don't need to include other files from the Firebird development kit, like ibase.h or iberror.h. ===== Linking ===== You also obviously need to link your project with the ibpp 'library' (actually with the IBPP source code files). ===== Connecting to an existing database ===== You will use one IBPP::[[Database]] instance per connection to a database. Of course, you can handle as many connections as you want, to a same database, or to multiple databases, on a single or multiple local or remote servers. try { IBPP::Database db = IBPP::DatabaseFactory("myserver", "/data/db.fdb", "username", "password"); db->Connect(); ... db->Disconnect(); } catch (IBPP::Exception& e) { cout << e.ErrorMessage(); } In the above code, the ''DatabaseFactory()'' call gets an interface pointer to a [[Database]] and assigns it to a smart pointer. This won't actually connect to the database. Then the ''Connect()'' method is called (through the smart pointer) and the connection is attempted with whatever server, database name, user, and password you provided. In case of failure, IBPP will throw an ''IBPP::[[exceptions|Exception]]''. Throwing C++ exception is the only mean of reporting errors. IBPP has no return value on most of its methods. ''Disconnects()'' closes the connection. You can later connect again by re-issuing a Connect(). Or you can recyle the 'db' variable to connect to establish another different connection: ... db = IBPP::DatabaseFactory("otherserver", "/local/otherdb.fdb", "username", "password"); db->Connect(); ... Should you forgot to ''Disconnect()'' before assigning the new interface, IBPP will take care of cleanup for you. Upon assignment of the new interface, the smart pointer will first ''Release()'' the previous one. If this was the last reference to that interface, the old interface itself will be destructed, and as part of this the connection will be cleanly closed, rolling back any pending transactions, and so on... ===== Using a transaction ===== Once connected to the database, you will start one or multiple transactions. This is the simplest way of starting a transaction : ... IBPP::Database db = IBPP::DatabaseFactory("myserver", "/data/db.fdb" "username", "password"); db->Connect(); IBPP::Transaction tr = IBPP::TransactionFactory(db); tr->Start(); ... tr->Commit(); // Or tr->Rollback(); ... The above code will start a default 'write' transaction. It uses defaults which correspond to: IBPP::Transaction tr = IBPP::TransactionFactory(db, IBPP::amWrite, IBPP::ilConcurrency, IBPP::lrWait); This is a transaction for writing, using the isolation level 'Concurrency', and a lock resolution of 'Wait'. This is the most common transaction in the Firebird world. See ibpp.h for other options. Look for the TAM, TIL and TLR enumerations, respectively for Access Mode, Isolation Level and Lock Resolution. ===== Executing statements ===== To act upon the database you also need one or multiple statements. The IBPP::Statement is the interface through which you will really work on the database. It will let you submit SQL statements for execution, will help providing parameters for these statements and will be your one-stop shop for reading the results of the statements. Here is a short partial sample, db is supposed Connected() and tr is supposed Started() : ... std::string fn; std::string ln; IBPP::Statement st = IBPP::StatementFactory(db, tr); st->Execute("SELECT firstname, lastname FROM sometable ORDER BY lastname"); while (st->Fetch()) { st->Get(1, fn); st->Get(2, ln); cout << fn.c_str() << " - " << ln.c_str() << "\n"; } ... Let's comment the above code. A Statement is tightly linked to the database and the transaction in the context of which it will run, hence the db and tr parameters to the TransactionFactory() method. A statement that does not need to run multiple times can be handled straight to Execute() method. That's what we did here. Being a SELECT the statement returns possibly zero, one or multiple rows of data. In all cases, we will get the rows of the result set using the Fetch() method of the statement. Each Fetch() will return the next row of results. The first Fetch returns the first row. It is not implicitly fetched. When there are no more rows (or none), Fetch() simply returns 'false'. Another example using query parameters: ... std::string fn; std::string ln; IBPP::Statement st = IBPP::StatementFactory(db, tr); st->Prepare("SELECT age, firstname, lastname FROM sometable ORDER BY lastname " "WHERE age = ?"); for (int age = 30; age < 40; age++) { cout << "People of age " << age << ":\n"; st->Set(1, age); st->Execute(); while (st->Fetch()) { st->Get(1, fn); st->Get(2, ln); cout << fn.c_str() << " - " << ln.c_str() << "\n"; } } ... In this example, we Prepare() the statement instead of calling Execute(). This will allow us to repetedly Execute() the same statement, without the overhead of the preparation. Before each Execute(), we set the value of the input parameters (the question marks).