mirror of
https://github.com/SOCI/soci.git
synced 2026-01-05 12:40:37 -06:00
[docs] Fix issues reported by markdownlint
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"default": true,
|
||||
"MD013": false,
|
||||
"MD026": false,
|
||||
"MD024": false,
|
||||
"MD029": {"style": "ordered"}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ The SOCI DB2 backend requires IBM DB2 Call Level Interface (CLI) library.
|
||||
|
||||
On Unix, before using the DB2 backend please make sure, that you have sourced DB2 profile into your environment:
|
||||
|
||||
```sh
|
||||
```console
|
||||
. ~/db2inst1/sqllib/db2profile
|
||||
```
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ The SOCI Oracle backend requires Oracle's `libclntsh` client library. Depending
|
||||
|
||||
Note that the SOCI library itself depends also on `libdl`, so the minimum set of libraries needed to compile a basic client program is:
|
||||
|
||||
```sh
|
||||
```console
|
||||
-lsoci_core -lsoci_oracle -ldl -lclntsh -lnnz10
|
||||
```
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ The SOCI PostgreSQL backend requires PostgreSQL's `libpq` client library.
|
||||
|
||||
Note that the SOCI library itself depends also on `libdl`, so the minimum set of libraries needed to compile a basic client program is:
|
||||
|
||||
```sh
|
||||
```console
|
||||
-lsoci_core -lsoci_postgresql -ldl -lpq
|
||||
```
|
||||
|
||||
|
||||
@@ -6,16 +6,18 @@ And, if this is still not enough, you can use the backend-specific methods direc
|
||||
|
||||
## Affected rows
|
||||
|
||||
It can be useful to know how many rows were affected by the last SQL statement, most often when using <tt>INSERT</tt>, <tt>UPDATE</tt> or <tt>DELETE</tt>.
|
||||
It can be useful to know how many rows were affected by the last SQL statement, most often when using `INSERT`, `UPDATE` or `DELETE`.
|
||||
SOCI provides `statement::get_affected_rows()` method allowing to do this:
|
||||
|
||||
statement st = (sql.prepare << "update some_table ...");
|
||||
st.execute(true);
|
||||
```cpp
|
||||
statement st = (sql.prepare << "update some_table ...");
|
||||
st.execute(true);
|
||||
|
||||
if ( !st.get_affected_rows() )
|
||||
{
|
||||
... investigate why no rows were modified ...
|
||||
}
|
||||
if ( !st.get_affected_rows() )
|
||||
{
|
||||
... investigate why no rows were modified ...
|
||||
}
|
||||
```
|
||||
|
||||
### Portability note
|
||||
|
||||
@@ -35,24 +37,26 @@ If you know which kind of database you use, you may use only one of them: when w
|
||||
|
||||
However if you use multiple SOCI backends or even just a single ODBC backend but support connecting to databases of different types, you actually must use both of them in the following way to insert a row:
|
||||
|
||||
long id;
|
||||
statement st;
|
||||
if ( sql.get_next_sequence_value("table_sequence", id) )
|
||||
{
|
||||
st << "insert into table(id, f1, f2) values(:id, :f1, :f2)",
|
||||
use(id), use(f1), use(f2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're not using sequences, so don't specify the value,
|
||||
// it will be automatically generated by the database on insert.
|
||||
st << "insert into table(f1, f2) value(:f1, :f2)",
|
||||
use(f1), use(f2);
|
||||
```cpp
|
||||
long id;
|
||||
statement st;
|
||||
if ( sql.get_next_sequence_value("table_sequence", id) )
|
||||
{
|
||||
st << "insert into table(id, f1, f2) values(:id, :f1, :f2)",
|
||||
use(id), use(f1), use(f2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're not using sequences, so don't specify the value,
|
||||
// it will be automatically generated by the database on insert.
|
||||
st << "insert into table(f1, f2) value(:f1, :f2)",
|
||||
use(f1), use(f2);
|
||||
|
||||
// If the ID used for the above row is needed later, get it:
|
||||
if ( !sql.get_last_insert_id("table", id) )
|
||||
... unexpected error, handle appropriately ...
|
||||
}
|
||||
// If the ID used for the above row is needed later, get it:
|
||||
if ( !sql.get_last_insert_id("table", id) )
|
||||
... unexpected error, handle appropriately ...
|
||||
}
|
||||
```
|
||||
|
||||
### Portability note
|
||||
|
||||
@@ -68,12 +72,14 @@ library does *not* prevent the client applications from reaching into the low-le
|
||||
Most of the SOCI classes have the `getBackEnd` method, which returns the pointer to the actual backend object that implements the given functionality.
|
||||
The knowledge of the actual backend allows the client application to get access to all low-level details that are involved.
|
||||
|
||||
blob b(sql);
|
||||
```cpp
|
||||
blob b(sql);
|
||||
|
||||
oracle_session_back_end * sessionBackEnd = static_cast<oracle_session_back_end *>(sql.get_back_end());
|
||||
oracle_blob_back_end * blobBackEnd = static_cast<oracle_blob_back_end *>(b.get_back_end());
|
||||
oracle_session_back_end * sessionBackEnd = static_cast<oracle_session_back_end *>(sql.get_back_end());
|
||||
oracle_blob_back_end * blobBackEnd = static_cast<oracle_blob_back_end *>(b.get_back_end());
|
||||
|
||||
OCILobDisableBuffering(sessionBackEnd->svchp_, sessionBackEnd->errhp_, blobBackEnd->lobp_);
|
||||
OCILobDisableBuffering(sessionBackEnd->svchp_, sessionBackEnd->errhp_, blobBackEnd->lobp_);
|
||||
```
|
||||
|
||||
The above example retrieves the `rowid` ("something" that identifies the row in the table) from the table and uses the `get_back_end` function to extract the actual object that implements this functionality.
|
||||
Assuming that it is the `"postgresql"` backend which is in use, the downcast is performed to use the `postgresql_rowid_back_end` interface to get the actual OID value that is a physical, low-level implementation of row identifier on PostgreSQL databases.
|
||||
|
||||
118
docs/binding.md
118
docs/binding.md
@@ -9,11 +9,13 @@ SOCI provides mechanisms to bind local buffers for input and output data.
|
||||
The `into` expression is used to add binding information to
|
||||
the statement:
|
||||
|
||||
int count;
|
||||
sql << "select count(*) from person", into(count);
|
||||
```cpp
|
||||
int count;
|
||||
sql << "select count(*) from person", into(count);
|
||||
|
||||
string name;
|
||||
sql << "select name from person where id = 7", into(name);
|
||||
string name;
|
||||
sql << "select name from person where id = 7", into(name);
|
||||
```
|
||||
|
||||
In the above examples, some data is retrieved from the database and transmitted *into* the given local variable.
|
||||
|
||||
@@ -23,40 +25,48 @@ There should be as many `into` elements as there are expected columns in the res
|
||||
|
||||
The `use` expression associates the SQL placeholder (written with colon) with the local data:
|
||||
|
||||
int val = 7;
|
||||
sql << "insert into numbers(val) values(:val)", use(val);
|
||||
```cpp
|
||||
int val = 7;
|
||||
sql << "insert into numbers(val) values(:val)", use(val);
|
||||
```
|
||||
|
||||
In the above statement, the first "val" is a column name (assuming that there is appropriate table `numbers` with this column), the second "val" (with colon) is a placeholder and its name is ignored here, and the third "val" is a name of local variable.
|
||||
|
||||
To better understand the meaning of each "val" above, consider also:
|
||||
|
||||
int number = 7;
|
||||
sql << "insert into numbers(val) values(:blabla)", use(number);
|
||||
```cpp
|
||||
int number = 7;
|
||||
sql << "insert into numbers(val) values(:blabla)", use(number);
|
||||
```
|
||||
|
||||
Both examples above will insert the value of some local variable into the table `numbers` - we say that the local variable is *used* in the SQL statement.
|
||||
|
||||
There should be as many `use` elements as there are parameters used in the SQL query.
|
||||
|
||||
### Object lifetime and immutability:
|
||||
### Object lifetime and immutability
|
||||
|
||||
SOCI assumes that local variables provided as `use` elements live at least as long at it takes to execute the whole statement.
|
||||
In short statement forms like above, the statement is executed *sometime* at the end of the full expression and the whole process is driven by the invisible temporary object handled by the library.
|
||||
If the data provided by user comes from another temporary variable, it might be possible for the compiler to arrange them in a way that the user data will be destroyed *before* the statement will have its chance to execute, referencing objects that no longer exist:
|
||||
|
||||
// Dangerous code!
|
||||
```cpp
|
||||
// Dangerous code!
|
||||
|
||||
string getNameFromSomewhere();
|
||||
string getNameFromSomewhere();
|
||||
|
||||
sql << "insert into person(name) values(:n)", use(getNameFromSomewhere());
|
||||
sql << "insert into person(name) values(:n)", use(getNameFromSomewhere());
|
||||
```
|
||||
|
||||
In the above example, the data passed to the database comes from the temporary variable that is a result of call to `getNameFromSomewhere` - this should be avoided and named variables should be used to ensure safe lifetime relations:
|
||||
|
||||
// Safe code
|
||||
|
||||
string getNameFromSomewhere();
|
||||
```cpp
|
||||
// Safe code
|
||||
|
||||
string name = getNameFromSomewhere();
|
||||
sql << "insert into person(name) values(:n)", use(name);
|
||||
string getNameFromSomewhere();
|
||||
|
||||
string name = getNameFromSomewhere();
|
||||
sql << "insert into person(name) values(:n)", use(name);
|
||||
```
|
||||
|
||||
It is still possible to provide `const` data for use elements.
|
||||
|
||||
@@ -64,12 +74,14 @@ Note that some database servers, like Oracle, allow PL/SQL procedures to modify
|
||||
|
||||
The above example can be ultimately written in the following way:
|
||||
|
||||
// Safe and efficient code
|
||||
```cpp
|
||||
// Safe and efficient code
|
||||
|
||||
string getNameFromSomewhere();
|
||||
string getNameFromSomewhere();
|
||||
|
||||
string const& name = getNameFromSomewhere();
|
||||
sql << "insert into person(name) values(:n)", use(name);
|
||||
string const& name = getNameFromSomewhere();
|
||||
sql << "insert into person(name) values(:n)", use(name);
|
||||
```
|
||||
|
||||
### Portability note
|
||||
|
||||
@@ -80,14 +92,16 @@ In order to compile SOCI with those old client libraries, define the `SOCI_POSTG
|
||||
|
||||
If there is more output or input "holes" in the single statement, it is possible to use many `into` and `use` expressions, separated by commas, where each expression will be responsible for the consecutive "hole" in the statement:
|
||||
|
||||
string firstName = "John", lastName = "Smith";
|
||||
int personId = 7;
|
||||
```cpp
|
||||
string firstName = "John", lastName = "Smith";
|
||||
int personId = 7;
|
||||
|
||||
sql << "insert into person(id, firstname, lastname) values(:id, :fn, :ln)",
|
||||
sql << "insert into person(id, firstname, lastname) values(:id, :fn, :ln)",
|
||||
use(personId), use(firstName), use(lastName);
|
||||
|
||||
sql << "select firstname, lastname from person where id = :id",
|
||||
sql << "select firstname, lastname from person where id = :id",
|
||||
into(firstName), into(lastName), use(personId);
|
||||
```
|
||||
|
||||
In the code above, the order of "holes" in the SQL statement and the order of `into` and `use` expression should match.
|
||||
|
||||
@@ -97,18 +111,22 @@ The SQL placeholders that have their names (with colon) can be bound by name to
|
||||
|
||||
This explicit naming allows to use different order of elements:
|
||||
|
||||
string firstName = "John", lastName = "Smith";
|
||||
int personId = 7;
|
||||
sql << "insert into person(id, firstname, lastname) values(:id, :fn, :ln)",
|
||||
use(firstName, "fn"), use(lastName, "ln"), use(personId, "id");
|
||||
```cpp
|
||||
string firstName = "John", lastName = "Smith";
|
||||
int personId = 7;
|
||||
sql << "insert into person(id, firstname, lastname) values(:id, :fn, :ln)",
|
||||
use(firstName, "fn"), use(lastName, "ln"), use(personId, "id");
|
||||
```
|
||||
|
||||
or bind the same local data to many "holes" at the same time:
|
||||
|
||||
string addr = "...";
|
||||
sql << "update person"
|
||||
" set mainaddress = :addr, contactaddress = :addr"
|
||||
" where id = 7",
|
||||
use(addr, "addr");
|
||||
```cpp
|
||||
string addr = "...";
|
||||
sql << "update person"
|
||||
" set mainaddress = :addr, contactaddress = :addr"
|
||||
" where id = 7",
|
||||
use(addr, "addr");
|
||||
```
|
||||
|
||||
### Portability notes
|
||||
|
||||
@@ -128,30 +146,36 @@ This allows the database backend to optimize access and data transfer and benefi
|
||||
|
||||
It is possible to `use` the vector as a data source:
|
||||
|
||||
std::vector<int> v;
|
||||
// ...
|
||||
sql << "insert into t ...", use(v);
|
||||
```cpp
|
||||
std::vector<int> v;
|
||||
// ...
|
||||
sql << "insert into t ...", use(v);
|
||||
```
|
||||
|
||||
as well as a destination:
|
||||
|
||||
std::vector<int> v;
|
||||
v.resize(100);
|
||||
sql << "select ...", into(v);
|
||||
```cpp
|
||||
std::vector<int> v;
|
||||
v.resize(100);
|
||||
sql << "select ...", into(v);
|
||||
```
|
||||
|
||||
In the latter case the initial size of the vector defines the maximum number of data elements that the user is willing to accept and after executing the query the vector will be automatically resized to reflect that actual number of rows that were read and transmitted.
|
||||
That is, the vector will be automatically shrunk if the amount of data that was available was smaller than requested.
|
||||
|
||||
It is also possible to operate on the chosen sub-range of the vector:
|
||||
|
||||
std::vector<int> v;
|
||||
// ...
|
||||
std::size_t begin = ...;
|
||||
std::size_t end = ...;
|
||||
sql << "insert into t ...", use(v, begin, end);
|
||||
```cpp
|
||||
std::vector<int> v;
|
||||
// ...
|
||||
std::size_t begin = ...;
|
||||
std::size_t end = ...;
|
||||
sql << "insert into t ...", use(v, begin, end);
|
||||
|
||||
// or:
|
||||
// or:
|
||||
|
||||
sql << "select ...", into(v, begin, end);
|
||||
sql << "select ...", into(v, begin, end);
|
||||
```
|
||||
|
||||
Above, only the sub-range of the vector is used for data transfer and in the case of `into` operation, the `end` variable will be automatically adjusted to reflect the amount of data that was actually transmitted, but the vector object as a whole will retain its initial size.
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@ The integration with Boost types is optional and is *not* enabled by default, wh
|
||||
|
||||
In order to enable the support for any of the above types, the user needs to either include one of these headers:
|
||||
|
||||
#include <boost-optional.h>
|
||||
#include <boost-tuple.h>
|
||||
#include <boost-fusion.h>
|
||||
#include <boost-gregorian-date.h>
|
||||
```cpp
|
||||
#include <boost-optional.h>
|
||||
#include <boost-tuple.h>
|
||||
#include <boost-fusion.h>
|
||||
#include <boost-gregorian-date.h>
|
||||
```
|
||||
|
||||
or to define the `SOCI_USE_BOOST` macro before including the `soci.h` main header file.
|
||||
|
||||
@@ -21,50 +23,54 @@ The `boost::optional<T>` objects can be used everywhere where the regular user p
|
||||
|
||||
Example:
|
||||
|
||||
boost::optional<string> name;
|
||||
sql << "select name from person where id = 7", into(name);
|
||||
|
||||
if (name.is_initialized())
|
||||
{
|
||||
// OK, the name was retrieved and is not-null
|
||||
cout << "The name is " << name.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// the name is null
|
||||
}
|
||||
```cpp
|
||||
boost::optional<string> name;
|
||||
sql << "select name from person where id = 7", into(name);
|
||||
|
||||
if (name.is_initialized())
|
||||
{
|
||||
// OK, the name was retrieved and is not-null
|
||||
cout << "The name is " << name.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// the name is null
|
||||
}
|
||||
```
|
||||
|
||||
The `boost::optional<T>` objects are fully supported for both `into` and `use` elements, in both single and vector forms. They can be also used for user-defined data types.
|
||||
|
||||
|
||||
## Boost.Tuple
|
||||
|
||||
`boost::tuple<T1, ...>` allows to work with whole rows of information and in some cases can be more convenient to use than the more dynamically-oriented `row` type.
|
||||
|
||||
boost::tuple<string, string, int> person;
|
||||
```cpp
|
||||
boost::tuple<string, string, int> person;
|
||||
|
||||
sql << "select name, phone, salary from persons where ...",
|
||||
sql << "select name, phone, salary from persons where ...",
|
||||
into(person);
|
||||
```
|
||||
|
||||
Tuples are supported for both `into` and `use` elements.
|
||||
They can be used with `rowset` as well.
|
||||
|
||||
Tuples can be also composed with `boost::optional<T>`
|
||||
|
||||
boost::tuple<string, boost::optional<string>, int> person;
|
||||
```cpp
|
||||
boost::tuple<string, boost::optional<string>, int> person;
|
||||
|
||||
sql << "select name, phone, salary from persons where ...",
|
||||
sql << "select name, phone, salary from persons where ...",
|
||||
into(person);
|
||||
|
||||
if (person.get<1>().is_initialized())
|
||||
{
|
||||
// the given person has a phone number
|
||||
}
|
||||
else
|
||||
{
|
||||
// this person does not have a phone number
|
||||
}
|
||||
if (person.get<1>().is_initialized())
|
||||
{
|
||||
// the given person has a phone number
|
||||
}
|
||||
else
|
||||
{
|
||||
// this person does not have a phone number
|
||||
}
|
||||
```
|
||||
|
||||
## Boost.Fusion
|
||||
|
||||
|
||||
@@ -9,11 +9,15 @@ which meaning is backend-dependent.
|
||||
|
||||
Example:
|
||||
|
||||
session sql(oracle, "service=orcl user=scott password=tiger");
|
||||
```cpp
|
||||
session sql(oracle, "service=orcl user=scott password=tiger");
|
||||
```
|
||||
|
||||
Another example might be:
|
||||
|
||||
session sql(postgresql, "dbname=mydb");
|
||||
```cpp
|
||||
session sql(postgresql, "dbname=mydb");
|
||||
```
|
||||
|
||||
Above, the `sql` object is a local (automatic) object that encapsulates the connection.
|
||||
|
||||
@@ -25,11 +29,15 @@ Dynamically loadable backends are compiled as shared libraries and allow to sele
|
||||
|
||||
The usage is similar to the above, but instead of providing the factory object, the backend name is expected:
|
||||
|
||||
session sql("postgresql", "dbname=mydb");
|
||||
```cpp
|
||||
session sql("postgresql", "dbname=mydb");
|
||||
```
|
||||
|
||||
For convenience, the URL-like form that combines both the backend name with connection parameters is supported as well:
|
||||
|
||||
session sql("postgresql://dbname=mydb");
|
||||
```cpp
|
||||
session sql("postgresql://dbname=mydb");
|
||||
```
|
||||
|
||||
The last two constructors described above try to locate the shared library with the name `libsoci_ABC.so` (or `libsoci_ABC.dll` on Windows), where ABC is the backend name.
|
||||
In the above examples, the expected library name will be `libsoci_postgresql.so` for Unix-like systems.
|
||||
@@ -39,9 +47,11 @@ Using this constructor is the only way to pass any non-default options to the ba
|
||||
|
||||
For example, to suppress any interactive prompts when using ODBC backend you could do:
|
||||
|
||||
connection_parameters parameters("odbc", "DSN=mydb");
|
||||
parameters.set_option(odbc_option_driver_complete, "0" /* SQL_DRIVER_NOPROMPT */);
|
||||
session sql(parameters);
|
||||
```cpp
|
||||
connection_parameters parameters("odbc", "DSN=mydb");
|
||||
parameters.set_option(odbc_option_driver_complete, "0" /* SQL_DRIVER_NOPROMPT */);
|
||||
session sql(parameters);
|
||||
```
|
||||
|
||||
Notice that you need to `#include<soci-odbc.h>` to obtain the option name declaration.
|
||||
The existing options are described in the backend-specific part of the documentation.
|
||||
@@ -60,27 +70,31 @@ The run-time selection of backends is also supported with libraries linked stati
|
||||
|
||||
Each backend provides a separate function of the form `register_factory_*name*`, where `*name*` is a backend name. Thus:
|
||||
|
||||
extern "C" void register_factory_postgresql();
|
||||
// ...
|
||||
register_factory_postgresql();
|
||||
session sql("postgresql://dbname=mydb");
|
||||
```cpp
|
||||
extern "C" void register_factory_postgresql();
|
||||
// ...
|
||||
register_factory_postgresql();
|
||||
session sql("postgresql://dbname=mydb");
|
||||
```
|
||||
|
||||
The above example registers the backend for PostgreSQL and later creates the session object for that backend.
|
||||
This form is provided for those projects that prefer static linking but still wish to benefit from run-time backend selection.
|
||||
|
||||
An alternative way to set up the session is to create it in the disconnected state and connect later:
|
||||
|
||||
session sql;
|
||||
```cpp
|
||||
session sql;
|
||||
|
||||
// some time later:
|
||||
sql.open(postgresql, "dbname=mydb");
|
||||
// some time later:
|
||||
sql.open(postgresql, "dbname=mydb");
|
||||
|
||||
// or:
|
||||
sql.open("postgresql://dbname=mydb");
|
||||
// or:
|
||||
sql.open("postgresql://dbname=mydb");
|
||||
|
||||
// or also:
|
||||
connection_parameters parameters("postgresql", "dbname=mydb");
|
||||
sql.open(parameters);
|
||||
// or also:
|
||||
connection_parameters parameters("postgresql", "dbname=mydb");
|
||||
sql.open(parameters);
|
||||
```
|
||||
|
||||
The rules for backend naming are the same as with the constructors described above.
|
||||
|
||||
@@ -109,28 +123,32 @@ The following backends are also available, with various levels of completeness:
|
||||
|
||||
The `failover_callback` interface can be used as a callback channel for notifications of events that are automatically processed when the session is forcibly closed due to connectivity problems. The user can override the following methods:
|
||||
|
||||
// Called when the failover operation has started,
|
||||
// after discovering connectivity problems.
|
||||
virtual void started();
|
||||
```cpp
|
||||
// Called when the failover operation has started,
|
||||
// after discovering connectivity problems.
|
||||
virtual void started();
|
||||
|
||||
// Called after successful failover and creating a new connection;
|
||||
// the sql parameter denotes the new connection and allows the user
|
||||
// to replay any initial sequence of commands (like session configuration).
|
||||
virtual void finished(session & sql);
|
||||
// Called after successful failover and creating a new connection;
|
||||
// the sql parameter denotes the new connection and allows the user
|
||||
// to replay any initial sequence of commands (like session configuration).
|
||||
virtual void finished(session & sql);
|
||||
|
||||
// Called when the attempt to reconnect failed,
|
||||
// if the user code sets the retry parameter to true,
|
||||
// then new connection will be attempted;
|
||||
// the newTarget connection string is a hint that can be ignored
|
||||
// by external means.
|
||||
virtual void failed(bool & retry, std::string & newTarget);
|
||||
|
||||
// Called when there was a failure that prevents further failover attempts.
|
||||
virtual void aborted();
|
||||
// Called when the attempt to reconnect failed,
|
||||
// if the user code sets the retry parameter to true,
|
||||
// then new connection will be attempted;
|
||||
// the newTarget connection string is a hint that can be ignored
|
||||
// by external means.
|
||||
virtual void failed(bool & retry, std::string & newTarget);
|
||||
|
||||
// Called when there was a failure that prevents further failover attempts.
|
||||
virtual void aborted();
|
||||
```
|
||||
|
||||
The user-provided callback implementation can be installed (or reset) with:
|
||||
|
||||
sql.set_failover_callback(myCallback);
|
||||
```cpp
|
||||
sql.set_failover_callback(myCallback);
|
||||
```
|
||||
|
||||
### Portability note
|
||||
|
||||
|
||||
110
docs/errors.md
110
docs/errors.md
@@ -3,17 +3,19 @@
|
||||
All DB-related errors manifest themselves as exceptions of type `soci_error`, which is derived from `std::runtime_error`.
|
||||
This allows to handle database errors within the standard exception framework:
|
||||
|
||||
int main()
|
||||
```cpp
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// regular code
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
cerr << "Bang! " << e.what() << endl;
|
||||
}
|
||||
// regular code
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
cerr << "Bang! " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `soci_error` class exposes two public functions:
|
||||
|
||||
@@ -31,61 +33,67 @@ If the error category is not recognized by the backend, it defaults to `unknown`
|
||||
|
||||
The MySQL backend can throw instances of the `mysql_soci_error`, which is publicly derived from `soci_error` and has an additional public `err_num_` member containing the MySQL error code (as returned by `mysql_errno()`):
|
||||
|
||||
int main()
|
||||
```cpp
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// regular code
|
||||
}
|
||||
catch (soci::mysql_soci_error const & e)
|
||||
{
|
||||
cerr << "MySQL error: " << e.err_num_
|
||||
<< " " << e.what() << endl;
|
||||
}
|
||||
catch (soci::exception const & e)
|
||||
{
|
||||
cerr << "Some other error: " << e.what() << endl;
|
||||
}
|
||||
// regular code
|
||||
}
|
||||
catch (soci::mysql_soci_error const & e)
|
||||
{
|
||||
cerr << "MySQL error: " << e.err_num_
|
||||
<< " " << e.what() << endl;
|
||||
}
|
||||
catch (soci::exception const & e)
|
||||
{
|
||||
cerr << "Some other error: " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Oracle
|
||||
|
||||
The Oracle backend can also throw the instances of the `oracle_soci_error`, which is publicly derived from `soci_error` and has an additional public `err_num_` member containing the Oracle error code:
|
||||
|
||||
int main()
|
||||
```cpp
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// regular code
|
||||
}
|
||||
catch (soci::oracle_soci_error const & e)
|
||||
{
|
||||
cerr << "Oracle error: " << e.err_num_
|
||||
<< " " << e.what() << endl;
|
||||
}
|
||||
catch (soci::exception const & e)
|
||||
{
|
||||
cerr << "Some other error: " << e.what() << endl;
|
||||
}
|
||||
// regular code
|
||||
}
|
||||
catch (soci::oracle_soci_error const & e)
|
||||
{
|
||||
cerr << "Oracle error: " << e.err_num_
|
||||
<< " " << e.what() << endl;
|
||||
}
|
||||
catch (soci::exception const & e)
|
||||
{
|
||||
cerr << "Some other error: " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## PostgreSQL
|
||||
|
||||
The PostgreSQL backend can also throw the instances of the `postgresql_soci_error`, which is publicly derived from `soci_error` and has an additional public `sqlstate()` member function returning the five-character "SQLSTATE" error code:
|
||||
|
||||
int main()
|
||||
```cpp
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// regular code
|
||||
}
|
||||
catch (soci::postgresql_soci_error const & e)
|
||||
{
|
||||
cerr << "PostgreSQL error: " << e.sqlstate()
|
||||
<< " " << e.what() << endl;
|
||||
}
|
||||
catch (soci::exception const & e)
|
||||
{
|
||||
cerr << "Some other error: " << e.what() << endl;
|
||||
}
|
||||
// regular code
|
||||
}
|
||||
catch (soci::postgresql_soci_error const & e)
|
||||
{
|
||||
cerr << "PostgreSQL error: " << e.sqlstate()
|
||||
<< " " << e.what() << endl;
|
||||
}
|
||||
catch (soci::exception const & e)
|
||||
{
|
||||
cerr << "Some other error: " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
70
docs/faq.md
70
docs/faq.md
@@ -4,7 +4,7 @@ This part of the documentation is supposed to gather in a single place the usual
|
||||
|
||||
## Q: Why "SOCI"?
|
||||
|
||||
SOCI was initially developed in the environment where Oracle was the main database technology in use. As a wrapper for the native OCI API (Oracle Call Interface), the name "Simple Oracle Call Interface" was quite obvious - until the 2.0 release, when the internal architecture was largely redesigned to allow the use of *backends* that support other database servers. We have kept the same name to indicate that Oracle is the main supported technology in the sense that the library includes only those features that were naturally implemented in Oracle. With the 2.1 release of the library, two new backends were added (MySQL and SQLite3) and we decided to drop the original full name so that new users looking for a library supporting any of these simpler libraries are not discouraged by seeing "Oracle" somewhere in the name.
|
||||
SOCI was initially developed in the environment where Oracle was the main database technology in use. As a wrapper for the native OCI API (Oracle Call Interface), the name "Simple Oracle Call Interface" was quite obvious - until the 2.0 release, when the internal architecture was largely redesigned to allow the use of *backends* that support other database servers. We have kept the same name to indicate that Oracle is the main supported technology in the sense that the library includes only those features that were naturally implemented in Oracle. With the 2.1 release of the library, two new backends were added (MySQL and SQLite3) and we decided to drop the original full name so that new users looking for a library supporting any of these simpler libraries are not discouraged by seeing "Oracle" somewhere in the name.
|
||||
|
||||
The other possible interpretation was "Syntax Oriented Call Interface", which stresses the fact that SOCI was built to support the most natural and easy interface for the user that is similar to the Embedded SQL concept (see below). But on the other hand, SOCI also provides other features (like object-relational mapping) and as a whole it is not just "emulator" of the Embedded SQL. With all these considerations in mind, SOCI is just "SOCI - The C++ Database Access Library".
|
||||
|
||||
@@ -15,28 +15,30 @@ that it's actually not that bad and the abstractions provided by the library are
|
||||
|
||||
The basic SOCI syntax was inspired by the Embedded SQL, which is part of the SQL standard, supported by the major DB technologies and even available as built-in part of the languages used in some DB-oriented integrated development environments. The term "Embedded SQL" is enough for Google to spit millions of references - one of the typical examples is:
|
||||
|
||||
{
|
||||
int a;
|
||||
/* ... */
|
||||
EXEC SQL SELECT salary INTO :a
|
||||
FROM Employee
|
||||
WHERE SSN=876543210;
|
||||
/* ... */
|
||||
printf("The salary is %d\n", a);
|
||||
/* ... */
|
||||
}
|
||||
```cpp
|
||||
{
|
||||
int a;
|
||||
/* ... */
|
||||
EXEC SQL SELECT salary INTO :a
|
||||
FROM Employee
|
||||
WHERE SSN=876543210;
|
||||
/* ... */
|
||||
printf("The salary is %d\n", a);
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
The above is not a regular C (nor C++) code, of course. It's the mix of C and SQL and there is a separate, pecialized preprocessor needed to convert it to something that the actual C (or C++) compiler will be able to understand. This means that the compilation of the program using embedded SQL is two-phase: preprocess the embedded SQL part and compile the result. This two-phase development is quite troublesome, especially when it comes to debugging. Yet, the advantage of it is that the code expresses the programmer's intents in a very straightforward
|
||||
way: read something from the database and put it into the local variable. Just like that.
|
||||
|
||||
|
||||
The SOCI library was born as an anwer to the following question: is it possible to have the same expressive power without the disadvantages of two-phase builds?
|
||||
|
||||
The following was chosen to be the basic SOCI syntax that can mirror the above Embedded SQL example:
|
||||
|
||||
int a;
|
||||
sql << "SELECT salary FROM Employee WHERE SSN=876543210", into(a);
|
||||
|
||||
```cpp
|
||||
int a;
|
||||
sql << "SELECT salary FROM Employee WHERE SSN=876543210", into(a);
|
||||
```
|
||||
|
||||
(as you see, SOCI changes the order of elements a little bit, so that the SQL query is separate and not mixed with other elements)
|
||||
|
||||
@@ -61,27 +63,31 @@ What is the most important, though, is that SOCI does not try to mess with the t
|
||||
|
||||
An example of the stream-like interface might be something like this (this is imaginary syntax, not supported by SOCI):
|
||||
|
||||
sql.exec("select a, b, c from some_table");
|
||||
```cpp
|
||||
sql.exec("select a, b, c from some_table");
|
||||
|
||||
while (!sql.eof())
|
||||
{
|
||||
int a, b, c;
|
||||
sql >> a >> b >> c;
|
||||
// ...
|
||||
}
|
||||
while (!sql.eof())
|
||||
{
|
||||
int a, b, c;
|
||||
sql >> a >> b >> c;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
We think that the data stored in the relational database should be treated as a set of relations - which is exactly what it is. This means that what is read from the database as a result of some SQL query is a *set of rows*. This set might be ordered, but it is still a set of rows, not a uniformly flat list of values. This distinction might seem to be unnecessarily low-level and that the uniform stream-like presentation of data is more preferable, but it's actually the other way round - the set of rows is something more structured - and that structure was *designed* into the database - than the flat stream and is therefore less prone to programming errors like miscounting the number of values that is expected in each row.
|
||||
|
||||
Consider the following programming error:
|
||||
|
||||
sql.exec("select a, b from some_table"); // only TWO columns
|
||||
```cpp
|
||||
sql.exec("select a, b from some_table"); // only TWO columns
|
||||
|
||||
while (!sql.eof())
|
||||
{
|
||||
int a, b, c;
|
||||
sql >> a >> b >> c; // this causes "row-tearing"
|
||||
// ...
|
||||
}
|
||||
while (!sql.eof())
|
||||
{
|
||||
int a, b, c;
|
||||
sql >> a >> b >> c; // this causes "row-tearing"
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
*"How to detect the end of each line in a file"* is a common beginner's question that relates to the use of IOStreams - and this common question clearly shows that for the record-oriented data the stream is not an optimal abstraction. Of course, we don't claim that IOStreams is bad - but we do insist that the record-oriented data is
|
||||
better manipulated in a way that is also record-aware.
|
||||
@@ -93,7 +99,6 @@ data returned from the database is still structured into rows, but each row can
|
||||
|
||||
Some programmers are used to indicating the null value by using some special (which means: "unlikely" to be ever used) value - for example, to use the smallest integer value to indicate null integer. Or to use empty string to indicate null string. And so on.
|
||||
|
||||
|
||||
We think that it's *completely wrong*. Null (in the database sense) is an information *about* the data. It describes the *state* of the data and if it's null, then there's *no data at all*. Nothing. Null. It does not make any sense to talk about some special value if in fact there is *no* value at all - especially if we take into account that, for example, the smallest integer value (or whatever else you choose as the "special" value) might not be *that* special in the given application or domain.
|
||||
|
||||
Thus, SOCI uses a separate indicators to describe the state of exchanged data. It also has an additional benefit of allowing the library to convey more than two states (null and not null). Indeed, the SOCI library uses indicators also to report that the data was read, but truncated (this applies to strings when reading to fixed-length character arrays). Truncation is also an information about the data and as such it's better to have it in addition to the data, not as part of it.
|
||||
@@ -112,14 +117,13 @@ Above, the "and" plays a role of the comma. Even if overloading the comma operat
|
||||
|
||||
Indeed, the `operator<<` in the basic SOCI syntax shows that something (the query) is *sent* somewhere (to the server). Some people don't like this, especially when the "select" statements are involved. If the high-level idea is to *read* data from somewhere, then `operator<<` seems unintuitive to the die-hard IOStreams users. The fact is, however, that the code containing SQL statement already indicates that there is a client-server relationship with some other software component (very likely remote). In such code it does not make any sense to pretend that the communication is one-way only, because it's clear that even the "select" statements need to be *sent* somewhere. This approach is also more uniform and allows to cover other statements like "drop table" or alike, where no data is expected to be exchanged at all (and therefore the IOStreams analogies for data exchange have no sense at all). No matter what is the kind of the SQL statement, it is *sent* to the server and this "sending" justifies the choice of `operator<<`.
|
||||
|
||||
|
||||
Using different operators (`operator>>` and `operator<<`) as a way of distinguishing between different high-level ideas (*reading* and *writing* from the data store, respectively) does make sense on much higher level of abstraction, where the SQL statement itself is already hidden - and we do encourage programmers to use SOCI for implementing such high-level abstractions. For this, the object-relational mapping facilities available in SOCI might prove to be a valuable tool as well, as an effective bridge
|
||||
between the low-level world of SQL statements and the high-level world of user-defined abstract data types.
|
||||
|
||||
##` Q: Why the Boost license?
|
||||
## Q: Why the Boost license?
|
||||
|
||||
We decided to use the [Boost license](http://www.boost.org/LICENSE_1_0.txt), because
|
||||
it's well recognized in the C++ community, allows us to keep our minimum copyrights, and at the same time allows SOCI to be safely used in commercial projects, without imposing concerns (or just plainuncertainty) typical to other open source licenses, like GPL. We also hope that by choosing the Boost license we have made the life easier
|
||||
for both us and our users. It saves us from answering law-related questions that were already answered on the [Boost license info page](http://www.boost.org/more/license_info.html) and it should also give more confidence to our users - especially to those of them, who already accepted the conditions of the Boost license - the just have one license less to analyze.
|
||||
|
||||
Still, if for any reason the conditions of this license are not acceptable, we encourage the users to contact us directly (see [links](http://soci.sourceforge.net/people.html) on the relevant SOCI page) to discuss any remaining concerns.
|
||||
Still, if for any reason the conditions of this license are not acceptable, we encourage the users to contact us directly (see [links](http://soci.sourceforge.net/people.html) on the relevant SOCI page) to discuss any remaining concerns.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# SOCI - The C++ Database Access Library
|
||||
|
||||
SOCI is a database access library written in C++ that makes an illusion of embedding
|
||||
SQL queries in the regular C++ code, staying entirely within the Standard C++.
|
||||
|
||||
@@ -8,37 +10,42 @@ If you find existing libraries too difficult for your needs or just distracting,
|
||||
|
||||
The simplest motivating code example for the SQL query that is supposed to retrieve a single row is:
|
||||
|
||||
int id = ...;
|
||||
string name;
|
||||
int salary;
|
||||
```cpp
|
||||
int id = ...;
|
||||
string name;
|
||||
int salary;
|
||||
|
||||
sql << "select name, salary from persons where id = " << id,
|
||||
sql << "select name, salary from persons where id = " << id,
|
||||
into(name), into(salary);
|
||||
```
|
||||
|
||||
## Basic ORM
|
||||
|
||||
The following benefits from extensive support for object-relational mapping:
|
||||
|
||||
int id = ...;
|
||||
Person p;
|
||||
```cpp
|
||||
int id = ...;
|
||||
Person p;
|
||||
|
||||
sql << "select first_name, last_name, date_of_birth "
|
||||
"from persons where id = " << id,
|
||||
into(p);
|
||||
sql << "select first_name, last_name, date_of_birth "
|
||||
"from persons where id = " << id, into(p);
|
||||
```
|
||||
|
||||
## Integrations
|
||||
|
||||
Integration with STL is also supported:
|
||||
|
||||
Rowset<string> rs = (sql.prepare << "select name from persons");
|
||||
std::copy(rs.begin(), rs.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
|
||||
```cpp
|
||||
Rowset<string> rs = (sql.prepare << "select name from persons");
|
||||
std::copy(rs.begin(), rs.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
|
||||
```
|
||||
|
||||
SOCI offers also extensive [integration with Boost](boost.md) datatypes (optional, tuple and fusion) and flexible support for user-defined datatypes.
|
||||
|
||||
## Database Backends
|
||||
|
||||
Starting from its 2.0.0 release, SOCI uses the plug-in architecture for backends
|
||||
- this allows to target various database servers.
|
||||
Starting from its 2.0.0 release, SOCI uses the plug-in architecture for
|
||||
backends - this allows to target various database servers.
|
||||
|
||||
Currently (SOCI 4.0.0), backends for following database systems are supported:
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
In order to support SQL NULL values and other conditions which are not real errors, the concept of *indicator* is provided.
|
||||
|
||||
## Select with NULLs
|
||||
## Select with NULL values
|
||||
|
||||
For example, when the following SQL query is executed:
|
||||
|
||||
select name from person where id = 7
|
||||
```sql
|
||||
select name from person where id = 7
|
||||
```
|
||||
|
||||
there are three possible outcomes:
|
||||
|
||||
@@ -18,56 +20,62 @@ Whereas the first alternative is easy to handle, the other two are more complex.
|
||||
Moreover, they are not necessarily errors from the application's point of view and what's more interesting, they are *different* and the application may wish to detect which is the case.
|
||||
The following example does this:
|
||||
|
||||
string name;
|
||||
indicator ind;
|
||||
```cpp
|
||||
string name;
|
||||
indicator ind;
|
||||
|
||||
sql << "select name from person where id = 7", into(name, ind);
|
||||
sql << "select name from person where id = 7", into(name, ind);
|
||||
|
||||
if (sql.got_data())
|
||||
if (sql.got_data())
|
||||
{
|
||||
switch (ind)
|
||||
{
|
||||
switch (ind)
|
||||
{
|
||||
case i_ok:
|
||||
// the data was returned without problems
|
||||
break;
|
||||
case i_null:
|
||||
// there is a person, but he has no name (his name is null)
|
||||
break;
|
||||
case i_truncated:
|
||||
// the name was returned only in part,
|
||||
// because the provided buffer was too short
|
||||
// (not possible with std::string, but possible with char* and char[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no such person in the database
|
||||
case i_ok:
|
||||
// the data was returned without problems
|
||||
break;
|
||||
case i_null:
|
||||
// there is a person, but he has no name (his name is null)
|
||||
break;
|
||||
case i_truncated:
|
||||
// the name was returned only in part,
|
||||
// because the provided buffer was too short
|
||||
// (not possible with std::string, but possible with char* and char[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no such person in the database
|
||||
}
|
||||
```
|
||||
|
||||
The use of indicator variable is optional, but if it is not used and the result would be `i_null`,
|
||||
then the exception is thrown.
|
||||
This means that you should use indicator variables everywhere where the application logic (and database schema) allow the "attribute not set" condition.
|
||||
|
||||
## Insert with NULLs
|
||||
## Insert with NULL values
|
||||
|
||||
Indicator variables can be also used when binding input data, to control whether the data is to be used as provided, or explicitly overrided to be null:
|
||||
|
||||
int id = 7;
|
||||
string name;
|
||||
indicator ind = i_null;
|
||||
sql << "insert into person(id, name) values(:id, :name)",
|
||||
```cpp
|
||||
int id = 7;
|
||||
string name;
|
||||
indicator ind = i_null;
|
||||
sql << "insert into person(id, name) values(:id, :name)",
|
||||
use(id), use(name, ind);
|
||||
```
|
||||
|
||||
In the above example, the row is inserted with `name` attribute set to null.
|
||||
|
||||
## Bulk operations with NULLs
|
||||
## Bulk operations with NULL values
|
||||
|
||||
Indicator variables can also be used in conjunction with vector based insert, update, and select statements:
|
||||
|
||||
vector<string> names(100);
|
||||
vector<indicator> inds;
|
||||
sql << "select name from person where id = 7", into(names, inds);
|
||||
```cpp
|
||||
vector<string> names(100);
|
||||
vector<indicator> inds;
|
||||
sql << "select name from person where id = 7", into(names, inds);
|
||||
```
|
||||
|
||||
The above example retrieves first 100 rows of data (or less).
|
||||
The initial size of `names` vector provides the (maximum) number of rows that should be read.
|
||||
@@ -75,18 +83,20 @@ Both vectors will be automatically resized according to the number of rows that
|
||||
|
||||
The following example inserts null for each value of name:
|
||||
|
||||
vector<int> ids;
|
||||
vector<string> names;
|
||||
vector<indicator> nameIndicators;
|
||||
```cpp
|
||||
vector<int> ids;
|
||||
vector<string> names;
|
||||
vector<indicator> nameIndicators;
|
||||
|
||||
for (int i = 0; i != 10; ++i)
|
||||
{
|
||||
ids.push_back(i);
|
||||
names.push_back("");
|
||||
nameIndicators.push_back(i_null);
|
||||
}
|
||||
for (int i = 0; i != 10; ++i)
|
||||
{
|
||||
ids.push_back(i);
|
||||
names.push_back("");
|
||||
nameIndicators.push_back(i_null);
|
||||
}
|
||||
|
||||
sql << "insert into person(id, name) values(:id, :name)",
|
||||
sql << "insert into person(id, name) values(:id, :name)",
|
||||
use(ids), use(name, nameIndicators);
|
||||
```
|
||||
|
||||
See also [Integration with Boost](boost.html) to learn how the Boost.Optional library can be used to handle null data conditions in a more natural way.
|
||||
|
||||
@@ -25,7 +25,9 @@ Unpack the archive.
|
||||
|
||||
You can always clone SOCI from the Git repository:
|
||||
|
||||
git clone git://github.com/SOCI/soci.git
|
||||
```console
|
||||
git clone git://github.com/SOCI/soci.git
|
||||
```
|
||||
|
||||
## Building with CMake
|
||||
|
||||
@@ -41,20 +43,24 @@ If you are new to CMake, you may find the tutorial [Running CMake](http://cmake.
|
||||
|
||||
Steps outline using GNU Make makefiles:
|
||||
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -G "Unix Makefiles" -DWITH_BOOST=OFF -DWITH_ORACLE=OFF (...) /path/to/soci-X.Y.Z
|
||||
$ make
|
||||
$ make install
|
||||
```console
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "Unix Makefiles" -DWITH_BOOST=OFF -DWITH_ORACLE=OFF (...) /path/to/soci-X.Y.Z
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
### Running CMake on Windows
|
||||
|
||||
Steps outline using Visual Studio 2010 and MSBuild:
|
||||
|
||||
> mkdir build
|
||||
> cd build
|
||||
> cmake -G "Visual Studio 10" -DWITH_BOOST=OFF -DWITH_ORACLE=OFF (...) C:\path\to\soci-X.Y.Z
|
||||
> msbuild.exe SOCI.sln
|
||||
```console
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "Visual Studio 10" -DWITH_BOOST=OFF -DWITH_ORACLE=OFF (...) C:\path\to\soci-X.Y.Z
|
||||
msbuild.exe SOCI.sln
|
||||
```
|
||||
|
||||
### CMake configuration
|
||||
|
||||
@@ -74,75 +80,73 @@ The lists consist of common variables for SOCI core and all backends as well as
|
||||
|
||||
List of a few essential CMake variables:
|
||||
|
||||
* CMAKE_BUILD_TYPE - string - Specifies the build type for make based generators (see CMake [help](http://cmake.org/cmake/help/cmake-2-8-docs.html#variable:CMAKE_BUILD_TYPE)).
|
||||
* CMAKE_INSTALL_PREFIX - path - Install directory used by install command (see CMake [help](http://cmake.org/cmake/help/cmake-2-8-docs.html#variable:CMAKE_INSTALL_PREFIX)).
|
||||
* CMAKE_VERBOSE_MAKEFILE - boolean - If ON, create verbose makefile (see CMake [help](http://cmake.org/cmake/help/cmake-2-8-docs.html#variable:CMAKE_VERBOSE_MAKEFILE)).
|
||||
* `CMAKE_BUILD_TYPE` - string - Specifies the build type for make based generators (see CMake [help](http://cmake.org/cmake/help/cmake-2-8-docs.html#variable:CMAKE_BUILD_TYPE)).
|
||||
* `CMAKE_INSTALL_PREFIX` - path - Install directory used by install command (see CMake [help](http://cmake.org/cmake/help/cmake-2-8-docs.html#variable:CMAKE_INSTALL_PREFIX)).
|
||||
* `CMAKE_VERBOSE_MAKEFILE` - boolean - If ON, create verbose makefile (see CMake [help](http://cmake.org/cmake/help/cmake-2-8-docs.html#variable:CMAKE_VERBOSE_MAKEFILE)).
|
||||
|
||||
List of variables to control common SOCI features and dependencies:
|
||||
|
||||
* SOCI_STATIC - boolean - Request to build static libraries, along with shared, of SOCI core and all successfully configured backends.
|
||||
* SOCI_TESTS - boolean - Request to build regression tests for SOCI core and all successfully configured backends.
|
||||
* WITH_BOOST - boolean - Should CMake try to detect [Boost C++ Libraries](http://www.boost.org/). If ON, CMake will try to find Boost headers and binaries of [Boost.Date_Time](http://www.boost.org/doc/libs/release/doc/html/date_time.html) library.
|
||||
|
||||
#### IBM DB2
|
||||
|
||||
* WITH_DB2 - boolean - Should CMake try to detect IBM DB2 Call Level Interface (CLI) library.
|
||||
* DB2_INCLUDE_DIR - string - Path to DB2 CLI include directories where CMake should look for `sqlcli1.h` header.
|
||||
* DB2_LIBRARIES - string - Full paths to `db2` or `db2api` libraries to link SOCI against to enable the backend support.
|
||||
* SOCI_DB2 - boolean - Requests to build [DB2](backends/db2.html) backend. Automatically switched on, if `WITH_DB2` is set to ON.
|
||||
* SOCI_DB2_TEST_CONNSTR - string - See [DB2 backend reference](backends/db2.html) for details. Example: `-DSOCI_DB2_TEST_CONNSTR:STRING="DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off"`
|
||||
|
||||
#### Firebird
|
||||
|
||||
* WITH_FIREBIRD - boolean - Should CMake try to detect Firebird client library.
|
||||
* FIREBIRD_INCLUDE_DIR - string - Path to Firebird include directories where CMake should look for `ibase.h` header.
|
||||
* FIREBIRD_LIBRARIES - string - Full paths to Firebird `fbclient` or `fbclient_ms` libraries to link SOCI against to enable the backend support.
|
||||
* SOCI_FIREBIRD - boolean - Requests to build [Firebird](backends/firebird.html) backend. Automatically switched on, if `WITH_FIREBIRD` is set to ON.
|
||||
* SOCI_FIREBIRD_TEST_CONNSTR - string - See [Firebird backend refernece](backends/firebird.html) for details. Example: `-DSOCI_FIREBIRD_TEST_CONNSTR:STRING="service=LOCALHOST:/tmp/soci_test.fdb user=SYSDBA password=masterkey"`
|
||||
|
||||
#### MySQL
|
||||
|
||||
* WITH_MYSQL - boolean - Should CMake try to detect [mysqlclient](http://dev.mysql.com/doc/refman/5.0/en/c.html) libraries providing MySQL C API. Note, currently the [mysql_config](http://dev.mysql.com/doc/refman/5.0/en/building-clients.html) program is not being used.
|
||||
* MYSQL_DIR - string - Path to MySQL installation root directory. CMake will scan subdirectories `MYSQL_DIR/include` and `MYSQL_DIR/lib` respectively for MySQL headers and libraries.
|
||||
* MYSQL_INCLUDE_DIR - string - Path to MySQL include directory where CMake should look for `mysql.h` header.
|
||||
* MYSQL_LIBRARIES - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* SOCI_MYSQL - boolean - Requests to build [MySQL](backends/mysql.html) backend. Automatically switched on, if `WITH_MYSQL` is set to ON.
|
||||
* SOCI_MYSQL_TEST_CONNSTR - string - Connection string to MySQL test database. Format of the string is explained [MySQL backend refernece](backends/mysql.html). Example: `-DSOCI_MYSQL_TEST_CONNSTR:STRING="db=mydb user=mloskot password=secret"`
|
||||
|
||||
#### ODBC
|
||||
|
||||
* WITH_ODBC - boolean - Should CMake try to detect ODBC libraries. On Unix systems, CMake tries to find [unixODBC](http://www.unixodbc.org/) or [iODBC](http://www.iodbc.org/) implementations.
|
||||
* ODBC_INCLUDE_DIR - string - Path to ODBC implementation include directories where CMake should look for `sql.h` header.
|
||||
* ODBC_LIBRARIES - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* SOCI_ODBC - boolean - Requests to build [ODBC](backends/odbc.html) backend. Automatically switched on, if `WITH_ODBC` is set to ON.
|
||||
* SOCI_ODBC_TEST_{database}_CONNSTR - string - ODBC Data Source Name (DSN) or ODBC File Data Source Name (FILEDSN) to test database: Microsoft Access (.mdb), Microsoft SQL Server, MySQL, PostgreSQL or any other ODBC SQL data source. {database} is placeholder for name of database driver ACCESS, MYSQL, POSTGRESQL, etc. See [ODBC](backends/odbc.html) backend refernece for details. Example: `-DSOCI_ODBC_TEST_POSTGRESQL_CONNSTR="FILEDSN=/home/mloskot/dev/soci/_git/build/test-postgresql.dsn"`
|
||||
|
||||
#### Oracle
|
||||
|
||||
* WITH_ORACLE - boolean - Should CMake try to detect [Oracle Call Interface (OCI)](http://en.wikipedia.org/wiki/Oracle_Call_Interface) libraries.
|
||||
* ORACLE_INCLUDE_DIR - string - Path to Oracle include directory where CMake should look for `oci.h` header.
|
||||
* ORACLE_LIBRARIES - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* SOCI_ORACLE - boolean - Requests to build [Oracle](backends/oracle.html) backend. Automatically switched on, if `WITH_ORACLE` is set to ON.
|
||||
* SOCI_ORACLE_TEST_CONNSTR - string - Connection string to Oracle test database. Format of the string is explained [Oracle backend reference](backends/oracle.html). Example: `-DSOCI_ORACLE_TEST_CONNSTR:STRING="service=orcl user=scott password=tiger"`
|
||||
|
||||
#### PostgreSQL
|
||||
|
||||
* WITH_POSTGRESQL - boolean - Should CMake try to detect PostgreSQL client interface libraries. SOCI relies on [libpq](http://www.postgresql.org/docs/9.0/interactive/libpq.html") C library.
|
||||
* POSTGRESQL_INCLUDE_DIR - string - Path to PostgreSQL include directory where CMake should look for `libpq-fe.h` header.
|
||||
* POSTGRESQL_LIBRARIES - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* SOCI_POSTGRESQL - boolean - Requests to build [PostgreSQL](backends/postgresql.html") backend. Automatically switched on, if `WITH_POSTGRESQL` is set to ON.
|
||||
* SOCI_POSTGRESQL_TEST_CONNSTR - boolean - Should CMak try to detect [SQLite C/C++ library](http://www.sqlite.org/cintro.html). As bonus, the configuration tries [OSGeo4W distribution](http://trac.osgeo.org/osgeo4w/) if `OSGEO4W_ROOT` environment variable is set.
|
||||
* SQLITE_INCLUDE_DIR - string - Path to SQLite 3 include directory where CMake should look for `sqlite3.h` header.
|
||||
* SQLITE_LIBRARIES - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* SOCI_SQLITE3 - boolean - Requests to build [SQLite3](backends/sqlite3.html) backend. Automatically switched on, if `WITH_SQLITE3` is set to ON.
|
||||
* SOCI_SQLITE3_TEST_CONNSTR - string - Connection string is simply a file path where SQLite3 test database will be created (e.g. /home/john/soci_test.db). Check [SQLite3 backend reference](backends/sqlite3.html) for details. Example: `-DSOCI_SQLITE3_TEST_CONNSTR="my.db"`
|
||||
* `SOCI_STATIC` - boolean - Request to build static libraries, along with shared, of SOCI core and all successfully configured backends.
|
||||
* `SOCI_TESTS` - boolean - Request to build regression tests for SOCI core and all successfully configured backends.
|
||||
* `WITH_BOOST` - boolean - Should CMake try to detect [Boost C++ Libraries](http://www.boost.org/). If ON, CMake will try to find Boost headers and binaries of [Boost.Date_Time](http://www.boost.org/doc/libs/release/doc/html/date_time.html) library.
|
||||
|
||||
#### Empty (sample backend)
|
||||
|
||||
* SOCI_EMPTY - boolean - Builds the [sample backend](backends.html) called Empty. Always ON by default.
|
||||
* SOCI_EMPTY_TEST_CONNSTR - string - Connection string used to run regression tests of the Empty backend. It is a dummy value. Example: `-DSOCI_EMPTY_TEST_CONNSTR="dummy connection"`
|
||||
* `SOCI_EMPTY` - boolean - Builds the [sample backend](backends.html) called Empty. Always ON by default.
|
||||
* `SOCI_EMPTY_TEST_CONNSTR` - string - Connection string used to run regression tests of the Empty backend. It is a dummy value. Example: `-DSOCI_EMPTY_TEST_CONNSTR="dummy connection"`
|
||||
|
||||
-----
|
||||
#### IBM DB2
|
||||
|
||||
* `WITH_DB2` - boolean - Should CMake try to detect IBM DB2 Call Level Interface (CLI) library.
|
||||
* `DB2_INCLUDE_DIR` - string - Path to DB2 CLI include directories where CMake should look for `sqlcli1.h` header.
|
||||
* `DB2_LIBRARIES` - string - Full paths to `db2` or `db2api` libraries to link SOCI against to enable the backend support.
|
||||
* `SOCI_DB2` - boolean - Requests to build [DB2](backends/db2.html) backend. Automatically switched on, if `WITH_DB2` is set to ON.
|
||||
* `SOCI_DB2_TEST_CONNSTR` - string - See [DB2 backend reference](backends/db2.html) for details. Example: `-DSOCI_DB2_TEST_CONNSTR:STRING="DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off"`
|
||||
|
||||
#### Firebird
|
||||
|
||||
* `WITH_FIREBIRD` - boolean - Should CMake try to detect Firebird client library.
|
||||
* `FIREBIRD_INCLUDE_DIR` - string - Path to Firebird include directories where CMake should look for `ibase.h` header.
|
||||
* `FIREBIRD_LIBRARIES` - string - Full paths to Firebird `fbclient` or `fbclient_ms` libraries to link SOCI against to enable the backend support.
|
||||
* `SOCI_FIREBIRD` - boolean - Requests to build [Firebird](backends/firebird.html) backend. Automatically switched on, if `WITH_FIREBIRD` is set to ON.
|
||||
* `SOCI_FIREBIRD_TEST_CONNSTR` - string - See [Firebird backend refernece](backends/firebird.html) for details. Example: `-DSOCI_FIREBIRD_TEST_CONNSTR:STRING="service=LOCALHOST:/tmp/soci_test.fdb user=SYSDBA password=masterkey"`
|
||||
|
||||
#### MySQL
|
||||
|
||||
* `WITH_MYSQL` - boolean - Should CMake try to detect [mysqlclient](http://dev.mysql.com/doc/refman/5.0/en/c.html) libraries providing MySQL C API. Note, currently the [mysql_config](http://dev.mysql.com/doc/refman/5.0/en/building-clients.html) program is not being used.
|
||||
* `MYSQL_DIR` - string - Path to MySQL installation root directory. CMake will scan subdirectories `MYSQL_DIR/include` and `MYSQL_DIR/lib` respectively for MySQL headers and libraries.
|
||||
* `MYSQL_INCLUDE_DIR` - string - Path to MySQL include directory where CMake should look for `mysql.h` header.
|
||||
* `MYSQL_LIBRARIES` - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* `SOCI_MYSQL` - boolean - Requests to build [MySQL](backends/mysql.html) backend. Automatically switched on, if `WITH_MYSQL` is set to ON.
|
||||
* `SOCI_MYSQL_TEST_CONNSTR` - string - Connection string to MySQL test database. Format of the string is explained [MySQL backend refernece](backends/mysql.html). Example: `-DSOCI_MYSQL_TEST_CONNSTR:STRING="db=mydb user=mloskot password=secret"`
|
||||
|
||||
#### ODBC
|
||||
|
||||
* `WITH_ODBC` - boolean - Should CMake try to detect ODBC libraries. On Unix systems, CMake tries to find [unixODBC](http://www.unixodbc.org/) or [iODBC](http://www.iodbc.org/) implementations.
|
||||
* `ODBC_INCLUDE_DIR` - string - Path to ODBC implementation include directories where CMake should look for `sql.h` header.
|
||||
* `ODBC_LIBRARIES` - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* `SOCI_ODBC` - boolean - Requests to build [ODBC](backends/odbc.html) backend. Automatically switched on, if `WITH_ODBC` is set to ON.
|
||||
* `SOCI_ODBC_TEST_{database}_CONNSTR` - string - ODBC Data Source Name (DSN) or ODBC File Data Source Name (FILEDSN) to test database: Microsoft Access (.mdb), Microsoft SQL Server, MySQL, PostgreSQL or any other ODBC SQL data source. {database} is placeholder for name of database driver ACCESS, MYSQL, POSTGRESQL, etc. See [ODBC](backends/odbc.html) backend refernece for details. Example: `-DSOCI_ODBC_TEST_POSTGRESQL_CONNSTR="FILEDSN=/home/mloskot/dev/soci/_git/build/test-postgresql.dsn"`
|
||||
|
||||
#### Oracle
|
||||
|
||||
* `WITH_ORACLE` - boolean - Should CMake try to detect [Oracle Call Interface (OCI)](http://en.wikipedia.org/wiki/Oracle_Call_Interface) libraries.
|
||||
* `ORACLE_INCLUDE_DIR` - string - Path to Oracle include directory where CMake should look for `oci.h` header.
|
||||
* `ORACLE_LIBRARIES` - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* `SOCI_ORACLE` - boolean - Requests to build [Oracle](backends/oracle.html) backend. Automatically switched on, if `WITH_ORACLE` is set to ON.
|
||||
* `SOCI_ORACLE_TEST_CONNSTR` - string - Connection string to Oracle test database. Format of the string is explained [Oracle backend reference](backends/oracle.html). Example: `-DSOCI_ORACLE_TEST_CONNSTR:STRING="service=orcl user=scott password=tiger"`
|
||||
|
||||
#### PostgreSQL
|
||||
|
||||
* `WITH_POSTGRESQL` - boolean - Should CMake try to detect PostgreSQL client interface libraries. SOCI relies on [libpq](http://www.postgresql.org/docs/9.0/interactive/libpq.html") C library.
|
||||
* `POSTGRESQL_INCLUDE_DIR` - string - Path to PostgreSQL include directory where CMake should look for `libpq-fe.h` header.
|
||||
* `POSTGRESQL_LIBRARIES` - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* `SOCI_POSTGRESQL` - boolean - Requests to build [PostgreSQL](backends/postgresql.html") backend. Automatically switched on, if `WITH_POSTGRESQL` is set to ON.
|
||||
* `SOCI_POSTGRESQL_TEST_CONNSTR` - boolean - Should CMak try to detect [SQLite C/C++ library](http://www.sqlite.org/cintro.html). As bonus, the configuration tries [OSGeo4W distribution](http://trac.osgeo.org/osgeo4w/) if `OSGEO4W_ROOT` environment variable is set.
|
||||
* `SQLITE_INCLUDE_DIR` - string - Path to SQLite 3 include directory where CMake should look for `sqlite3.h` header.
|
||||
* `SQLITE_LIBRARIES` - string - Full paths to libraries to link SOCI against to enable the backend support.
|
||||
* `SOCI_SQLITE3` - boolean - Requests to build [SQLite3](backends/sqlite3.html) backend. Automatically switched on, if `WITH_SQLITE3` is set to ON.
|
||||
* `SOCI_SQLITE3_TEST_CONNSTR` - string - Connection string is simply a file path where SQLite3 test database will be created (e.g. /home/john/soci_test.db). Check [SQLite3 backend reference](backends/sqlite3.html) for details. Example: `-DSOCI_SQLITE3_TEST_CONNSTR="my.db"`
|
||||
|
||||
## Building with Makefiles on Unix
|
||||
|
||||
@@ -154,18 +158,20 @@ The classic set of Makefiles for Unix/Linux systems is provided for those users
|
||||
In this sense, the basic Makefiles are supposed to provide a minimal starting point for custom experimentation and are not intended to be a complete build/installation solution.
|
||||
At the same time, they are complete in the sense that they can compile the library with all test programs and for some users this level of support will be just fine.
|
||||
|
||||
The `core` directory of the library distribution contains the `Makefile.basic` that can be used to compile the core part of the library.
|
||||
The `core` directory of the library distribution contains the `Makefile.basic` that can be used to compile the core part of the library.
|
||||
Run `make -f Makefile.basic` or `make -f Makefile.basic shared` to get the static and shared versions, respectively.
|
||||
Similarly, the `backends/<i>name</i>` directory contains the backend part for each supported backend with the appropriate `Makefile.basic` and the `backends/<i>name</i>/test` directory contains the test program for the given backend.
|
||||
|
||||
For example, the simplest way to compile the static version of the library and the test program for PostgreSQL is:
|
||||
|
||||
$ cd src/core
|
||||
$ make -f Makefile.basic
|
||||
$ cd ../backends/postgresql
|
||||
$ make -f Makefile.basic
|
||||
$ cd test
|
||||
$ make -f Makefile.basic
|
||||
```console
|
||||
cd src/core
|
||||
make -f Makefile.basic
|
||||
cd ../backends/postgresql
|
||||
make -f Makefile.basic
|
||||
cd test
|
||||
make -f Makefile.basic
|
||||
```
|
||||
|
||||
For each backend and its test program, the `Makefile.basic`s contain the variables that can have values specific to the given environment - they usually name the include and library paths.
|
||||
These variables are placed at the beginning of the `Makefile.basic`s.
|
||||
@@ -185,17 +191,20 @@ Also, specify `SOCI_{backend name}_TEST_CONNSTR` variables to tell the tests run
|
||||
|
||||
Dedicated `make test` target can be used to execute regression tests on build completion:
|
||||
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -G "Unix Makefiles" -DWITH_BOOST=OFF \
|
||||
-DSOCI_TESTS=ON \
|
||||
-DSOCI_EMPTY_TEST_CONNSTR="dummy connection" \
|
||||
-DSOCI_SQLITE3_TEST_CONNSTR="test.db" \
|
||||
(...)
|
||||
../soci-X.Y.Z
|
||||
$ make
|
||||
$ make test
|
||||
$ make install
|
||||
```console
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "Unix Makefiles" \
|
||||
-DWITH_BOOST=OFF \
|
||||
-DSOCI_TESTS=ON \
|
||||
-DSOCI_EMPTY_TEST_CONNSTR="dummy connection" \
|
||||
-DSOCI_SQLITE3_TEST_CONNSTR="test.db" \
|
||||
(...)
|
||||
../soci-X.Y.Z
|
||||
make
|
||||
make test
|
||||
make install
|
||||
```
|
||||
|
||||
In the example above, regression tests for the sample Empty backend and SQLite 3 backend are configured for execution by `make test` target.
|
||||
|
||||
|
||||
@@ -7,29 +7,33 @@ One of the major features of SOCI, although not immediately visible, is the vari
|
||||
The most exposed and promoted interface supports the syntax sugar that makes SOCI similar in look and feel to embedded SQL.
|
||||
The example of application code using this interface is:
|
||||
|
||||
session sql("postgresql://dbname=mydb");
|
||||
```cpp
|
||||
session sql("postgresql://dbname=mydb");
|
||||
|
||||
int id = 123;
|
||||
string name;
|
||||
int id = 123;
|
||||
string name;
|
||||
|
||||
sql << "select name from persons where id = :id", into(name), use(id);
|
||||
sql << "select name from persons where id = :id", into(name), use(id);
|
||||
```
|
||||
|
||||
## Core
|
||||
|
||||
The above example is equivalent to the following, more explicit sequence of calls:
|
||||
|
||||
session sql("postgresql://dbname=mydb");
|
||||
```cpp
|
||||
session sql("postgresql://dbname=mydb");
|
||||
|
||||
int id = 123;
|
||||
string name;
|
||||
int id = 123;
|
||||
string name;
|
||||
|
||||
statement st(sql);
|
||||
st.exchange(into(name));
|
||||
st.exchange(use(id));
|
||||
st.alloc();
|
||||
st.prepare("select name from persons where id = :id");
|
||||
st.define_and_bind();
|
||||
st.execute(true);
|
||||
statement st(sql);
|
||||
st.exchange(into(name));
|
||||
st.exchange(use(id));
|
||||
st.alloc();
|
||||
st.prepare("select name from persons where id = :id");
|
||||
st.define_and_bind();
|
||||
st.execute(true);
|
||||
```
|
||||
|
||||
The value of the *core* interface is that it is the basis for all other interfaces, and can be also used by developers to easily prepare their own convenience interfaces.
|
||||
Users who cannot or for some reason do not want to use the natural *sugar* interface should try the *core* one as the foundation and access point to all SOCI functionality.
|
||||
@@ -44,36 +48,37 @@ To avoid exceptions passing the module boundaries, all errors are reported as st
|
||||
|
||||
The above examples can be rewritten as (without error-handling):
|
||||
|
||||
#include <soci-simple.h>
|
||||
```cpp
|
||||
#include <soci-simple.h>
|
||||
|
||||
// ...
|
||||
session_handle sql = soci_create_session("postgresql://dbname=mydb");
|
||||
// ...
|
||||
session_handle sql = soci_create_session("postgresql://dbname=mydb");
|
||||
|
||||
statement_handle st = soci_create_statement(sql);
|
||||
statement_handle st = soci_create_statement(sql);
|
||||
|
||||
soci_use_int(st, "id");
|
||||
soci_set_use_int(st, "id", 123);
|
||||
soci_use_int(st, "id");
|
||||
soci_set_use_int(st, "id", 123);
|
||||
|
||||
int namePosition = soci_into_string(st);
|
||||
int namePosition = soci_into_string(st);
|
||||
|
||||
soci_prepare(st, "select name from persons where id = :id");
|
||||
soci_prepare(st, "select name from persons where id = :id");
|
||||
|
||||
soci_execute(st, true);
|
||||
soci_execute(st, true);
|
||||
|
||||
char const * name = soci_get_into_string(st, namePosition);
|
||||
char const * name = soci_get_into_string(st, namePosition);
|
||||
|
||||
printf("name is %s\n", name);
|
||||
|
||||
soci_destroy_statement(st);
|
||||
soci_destroy_session(sql);
|
||||
printf("name is %s\n", name);
|
||||
|
||||
soci_destroy_statement(st);
|
||||
soci_destroy_session(sql);
|
||||
```
|
||||
|
||||
The *simple* interface supports single and bulk data exchange for static binding.
|
||||
Dynamic row description is not supported in this release.
|
||||
|
||||
See [Simple client interface](/reference.html#simpleclient) reference documentation for more details.
|
||||
|
||||
#### Low-level backend interface
|
||||
## Low-level backend interface
|
||||
|
||||
The low-level backend interface allows to interact with backends directly and in principle allows to access the database without involving any other component.
|
||||
There is no particular reason to use this interface in the user code.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SOCI-Ada - manual
|
||||
# SOCI-Ada Bindings
|
||||
|
||||
## Concepts
|
||||
|
||||
@@ -12,7 +12,6 @@ There are two kinds of objects that can be managed by the SOCI-Ada library:
|
||||
|
||||
* *Use elements*, which are data objects that are transferred from the user program to the database as parameters of the query (and, if supported by the target database, that can be modified by the database server and transferred back to the user program). There are single use elements for binding parameters of single-row queries and vector use elements for binding whole bunches of data for transfer. The use elements are identified by their *name*.
|
||||
|
||||
|
||||
The user program can read the current value of into and use elements and assign new values to use elements. Elements are strongly typed and the following types are currently supported:
|
||||
|
||||
* `String`
|
||||
@@ -29,4 +28,4 @@ All statements are handled within the context of some *session*, which also supp
|
||||
|
||||
Sessions can be managed in isolation or as a group called *connection pool*, which helps to decouple tasking design choices from the concurrency policies at the database connection level. Sessions are *leased* from the pool for some time during which no other task can access them and returned back when no longer needed, where they can be acquired again by other tasks.
|
||||
|
||||
All potential problems are signalled via exceptions that have some descriptive message attached to them.
|
||||
All potential problems are signalled via exceptions that have some descriptive message attached to them.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# SOCI-Ada - manual
|
||||
|
||||
## Common Idioms
|
||||
# SOCI-Ada Bindings - Idioms
|
||||
|
||||
As any other library, SOCI-Ada has its set of idioms that ensure optimal work in terms of performance and resource usage. Still, the optimal use will depend on the concrete usage scenario - the places where programmer choices are needed will be described explicitly.
|
||||
|
||||
@@ -10,251 +8,228 @@ The idioms below are provided as *complete programs* with the intent to make the
|
||||
|
||||
This type of query is useful for DDL commands and can be executed directly on the given session, without explicit statement management.
|
||||
|
||||
```ada
|
||||
with SOCI;
|
||||
|
||||
with SOCI;
|
||||
procedure My_Program is
|
||||
|
||||
procedure My_Program is
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
begin
|
||||
|
||||
begin
|
||||
SQL.Execute ("drop table some_table");
|
||||
|
||||
SQL.Execute ("drop table some_table");
|
||||
end My_Program;
|
||||
|
||||
end My_Program;
|
||||
|
||||
<div class="note">
|
||||
<span class="note">Note:</span>
|
||||
|
||||
The session object is initialized by a constructor function call. An alternative would be to declare it without initialization and later use the `Open` operation to establish a physical connection with the database.
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
Note: The session object is initialized by a constructor function call. An alternative would be to declare it without initialization and later use the `Open` operation to establish a physical connection with the database.
|
||||
|
||||
## Simple query without parameters resulting in one row of data
|
||||
|
||||
This type of query requires only single into elements, which together with the statement have to be manipulated explicitly.
|
||||
|
||||
```ada
|
||||
with SOCI;
|
||||
with Ada.Text_IO;
|
||||
|
||||
with SOCI;
|
||||
with Ada.Text_IO;
|
||||
procedure My_Program is
|
||||
|
||||
procedure My_Program is
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
Pos : SOCI.Into_Position;
|
||||
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
Pos : SOCI.Into_Position;
|
||||
Num_Of_Persons : SOCI.DB_Integer;
|
||||
|
||||
Num_Of_Persons : SOCI.DB_Integer;
|
||||
begin
|
||||
|
||||
begin
|
||||
Pos := St.Into_Integer;
|
||||
St.Prepare ("select count(*) from persons");
|
||||
St.Execute (True);
|
||||
|
||||
Pos := St.Into_Integer;
|
||||
St.Prepare ("select count(*) from persons");
|
||||
St.Execute (True);
|
||||
Num_Of_Persons := St.Get_Into_Integer (Pos);
|
||||
|
||||
Num_Of_Persons := St.Get_Into_Integer (Pos);
|
||||
Ada.Text_IO.Put_Line ("Number of persons: " & SOCI.DB_Integer'Image (Num_Of_Persons));
|
||||
|
||||
Ada.Text_IO.Put_Line ("Number of persons: " & SOCI.DB_Integer'Image (Num_Of_Persons));
|
||||
|
||||
end My_Program;
|
||||
|
||||
<div class="note">
|
||||
<span class="note">Note:</span>
|
||||
|
||||
The into element is inspected by providing the position value that was obtained at the time if was created. No operations are defined for the position type. There can be many into elements with a single query.
|
||||
|
||||
</div>
|
||||
end My_Program;
|
||||
```
|
||||
|
||||
Note: The into element is inspected by providing the position value that was obtained at the time if was created. No operations are defined for the position type. There can be many into elements with a single query.
|
||||
|
||||
## Simple query with parameters and without results
|
||||
|
||||
This type of query requires only use elements.
|
||||
|
||||
with SOCI;
|
||||
```ada
|
||||
with SOCI;
|
||||
|
||||
procedure My_Program is
|
||||
procedure My_Program is
|
||||
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
|
||||
begin
|
||||
begin
|
||||
|
||||
St.Use_Integer ("increase");
|
||||
St.Set_Use_Integer ("increase", 1000);
|
||||
St.Use_Integer ("increase");
|
||||
St.Set_Use_Integer ("increase", 1000);
|
||||
|
||||
St.Prepare ("update persons set salary = salary + :increase");
|
||||
St.Execute (True);
|
||||
St.Prepare ("update persons set salary = salary + :increase");
|
||||
St.Execute (True);
|
||||
|
||||
end My_Program;
|
||||
|
||||
<div class="note">
|
||||
<span class="note">Note:</span>
|
||||
|
||||
The "`:increase`" in the query is a placeholder variable. There can be many such variables and each of them needs to be filled in by respective use element.
|
||||
|
||||
</div>
|
||||
end My_Program;
|
||||
```
|
||||
|
||||
Note: The "`:increase`" in the query is a placeholder variable. There can be many such variables and each of them needs to be filled in by respective use element.
|
||||
|
||||
## Repeated query with parameters and without results
|
||||
|
||||
This type of query requires only use elements, but they can be set differently for each statement execution.
|
||||
|
||||
```ada
|
||||
with SOCI;
|
||||
|
||||
with SOCI;
|
||||
procedure My_Program is
|
||||
|
||||
procedure My_Program is
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
begin
|
||||
|
||||
begin
|
||||
St.Use_String ("name");
|
||||
|
||||
St.Use_String ("name");
|
||||
St.Prepare ("insert into countries(country_name) values(:name)");
|
||||
|
||||
St.Prepare ("insert into countries(country_name) values(:name)");
|
||||
St.Set_Use_String ("name", "Poland");
|
||||
St.Execute (True);
|
||||
|
||||
St.Set_Use_String ("name", "Poland");
|
||||
St.Execute (True);
|
||||
St.Set_Use_String ("name", "Switzerland");
|
||||
St.Execute (True);
|
||||
|
||||
St.Set_Use_String ("name", "Switzerland");
|
||||
St.Execute (True);
|
||||
St.Set_Use_String ("name", "France");
|
||||
St.Execute (True);
|
||||
|
||||
St.Set_Use_String ("name", "France");
|
||||
St.Execute (True);
|
||||
end My_Program;
|
||||
```
|
||||
|
||||
end My_Program;
|
||||
|
||||
<div class="note">
|
||||
<span class="note">Note:</span>
|
||||
|
||||
Each time the query is executed, the *current* values of use elements are transferred to the database.
|
||||
|
||||
</div>
|
||||
Note: Each time the query is executed, the *current* values of use elements are transferred to the database.
|
||||
|
||||
## Batch query with parameters and without results
|
||||
|
||||
This type of query requires vector use elements. Compare with the previous example.
|
||||
|
||||
with SOCI;
|
||||
```ada
|
||||
with SOCI;
|
||||
|
||||
procedure My_Program is
|
||||
procedure My_Program is
|
||||
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
First : SOCI.Vector_Index;
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
First : SOCI.Vector_Index;
|
||||
|
||||
use type SOCI.Vector_Index;
|
||||
use type SOCI.Vector_Index;
|
||||
|
||||
begin
|
||||
begin
|
||||
|
||||
St.Use_Vector_String ("name");
|
||||
St.Use_Vector_String ("name");
|
||||
|
||||
St.Use_Vectors_Resize (3);
|
||||
St.Use_Vectors_Resize (3);
|
||||
|
||||
First := St.Use_Vectors_First_Index;
|
||||
First := St.Use_Vectors_First_Index;
|
||||
|
||||
St.Set_Use_Vector_String ("name", First + 0, "Poland");
|
||||
St.Set_Use_Vector_String ("name", First + 1, "Switzerland");
|
||||
St.Set_Use_Vector_String ("name", First + 2, "France");
|
||||
St.Set_Use_Vector_String ("name", First + 0, "Poland");
|
||||
St.Set_Use_Vector_String ("name", First + 1, "Switzerland");
|
||||
St.Set_Use_Vector_String ("name", First + 2, "France");
|
||||
|
||||
St.Prepare ("insert into countries(country_name) values(:name)");
|
||||
St.Execute (True);
|
||||
St.Prepare ("insert into countries(country_name) values(:name)");
|
||||
St.Execute (True);
|
||||
|
||||
end My_Program;
|
||||
end My_Program;
|
||||
```
|
||||
|
||||
<div class="note">
|
||||
<span class="note">Note:</span>
|
||||
Note:
|
||||
|
||||
The whole bunch of data is transferred to the database if the target database server supports it and the statement is automatically repeated otherwise. This is the preferred way to transfer many rows of data to the server when the data for all rows are known before executing the query.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="note">
|
||||
<span class="note">Note:</span>
|
||||
Note:
|
||||
|
||||
The query can be executed many times and each time a new batch of data can be transferred to the server. The size of the batch (set by calling `Use_Vectors_Resize`) can be different each time the query is executed, but cannot be larger than the size that was used the first time. The size of the batch defines a tradeoff between the amount of data being transmitted in a single step (this influences the memory used by the user program and the time of a single call) and the number of executions required to handle big data sets. The optimal size of the batch will therefore differ depending on the application, but in general tens of thousands is a reasonable limit for a batch size - the performance of the whole operation is usually not affected above this value so there is no need to imply higher memory usage at the client side.
|
||||
|
||||
</div>
|
||||
|
||||
## Simple query with many rows of results
|
||||
|
||||
This type of query requires simple into elements.
|
||||
|
||||
with SOCI;
|
||||
with Ada.Text_IO;
|
||||
```ada
|
||||
with SOCI;
|
||||
with Ada.Text_IO;
|
||||
|
||||
procedure My_Program is
|
||||
procedure My_Program is
|
||||
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
Pos : SOCI.Into_Position;
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
Pos : SOCI.Into_Position;
|
||||
|
||||
begin
|
||||
begin
|
||||
|
||||
Pos := St.Into_String;
|
||||
Pos := St.Into_String;
|
||||
|
||||
St.Prepare ("select country_name from countries");
|
||||
St.Execute;
|
||||
St.Prepare ("select country_name from countries");
|
||||
St.Execute;
|
||||
|
||||
while St.Fetch loop
|
||||
while St.Fetch loop
|
||||
|
||||
Ada.Text_IO.Put_Line (St.Get_Into_String (Pos));
|
||||
Ada.Text_IO.Put_Line (St.Get_Into_String (Pos));
|
||||
|
||||
end loop;
|
||||
end loop;
|
||||
|
||||
end My_Program;
|
||||
end My_Program;
|
||||
```
|
||||
|
||||
<div class="note">
|
||||
<span class="note">Note:</span>
|
||||
Note:
|
||||
|
||||
The loop above executes as many times as there are rows in the result. After each row is read, the into elements contain the respective values from that row. The `Execute` operation is called without parameter, which is `False` by default, meaning that no data transfer is intended. The data is being transferred only during the `Fetch` operation, which returns `False` when no data has been retrieved and the result is exhausted.
|
||||
|
||||
This type of query can have simple parameters which are fixed at the execution time.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Batch query with many rows of results
|
||||
|
||||
This type of query requires vector into elements. Compare with previous example.
|
||||
|
||||
with SOCI;
|
||||
with Ada.Text_IO;
|
||||
```ada
|
||||
with SOCI;
|
||||
with Ada.Text_IO;
|
||||
|
||||
procedure My_Program is
|
||||
procedure My_Program is
|
||||
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
Pos : SOCI.Into_Position;
|
||||
SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
|
||||
St : SOCI.Statement := SOCI.Make_Statement (SQL);
|
||||
Pos : SOCI.Into_Position;
|
||||
|
||||
Batch_Size : constant := 10;
|
||||
Batch_Size : constant := 10;
|
||||
|
||||
begin
|
||||
begin
|
||||
|
||||
Pos := St.Into_Vector_String;
|
||||
St.Into_Vectors_Resize (Batch_Size);
|
||||
Pos := St.Into_Vector_String;
|
||||
St.Into_Vectors_Resize (Batch_Size);
|
||||
|
||||
St.Prepare ("select country_name from countries");
|
||||
St.Execute;
|
||||
St.Prepare ("select country_name from countries");
|
||||
St.Execute;
|
||||
|
||||
while St.Fetch loop
|
||||
while St.Fetch loop
|
||||
|
||||
for I in St.Into_Vectors_First_Index .. St.Into_Vectors_Last_Index loop
|
||||
for I in St.Into_Vectors_First_Index .. St.Into_Vectors_Last_Index loop
|
||||
|
||||
Ada.Text_IO.Put_Line (St.Get_Into_Vector_String (Pos, I));
|
||||
Ada.Text_IO.Put_Line (St.Get_Into_Vector_String (Pos, I));
|
||||
|
||||
end loop;
|
||||
end loop;
|
||||
|
||||
St.Into_Vectors_Resize (Batch_Size);
|
||||
St.Into_Vectors_Resize (Batch_Size);
|
||||
|
||||
end loop;
|
||||
end loop;
|
||||
|
||||
end My_Program;
|
||||
end My_Program;
|
||||
```
|
||||
|
||||
|
||||
##### Note:
|
||||
Note:
|
||||
|
||||
The loop above is nested. The outer `while` loop fetches consecutive batches of rows from the database with requested batch size; the returned batch can be smaller than requested (the into vector elements are downsized automatically if needed) and the intended batch size is requested again before repeating the `Fetch` operation. For each returned batch, the into vector elements are inspected in the inner `for` loop. This scheme ensures correct operation independently on the size of returned batch and is therefore a recommended idiom for efficiently returning many rows of data.
|
||||
|
||||
@@ -262,7 +237,6 @@ There is a tradeoff between efficiency and memory usage and this tradeoff is con
|
||||
|
||||
This type of query can have simple (not vectors) parameters that are fixed at execution time.
|
||||
|
||||
|
||||
##### Final note:
|
||||
## Final note
|
||||
|
||||
Follow good database usage principles and avoid constructing queries by concatenating strings computed at run-time. Thanks to a good type system Ada is much better in preventing various SQL-injection attacks than weaker languages like PHP, but there is still a potential for vulnerability or at least performance loss. As a rule of thumb, rely on *use elements* to parameterize your queries and to provide clean separation between data and code. This will prevent many security vulnerabilities and will allow some servers to optimize their work by reusing already cached execution plans.
|
||||
|
||||
@@ -22,9 +22,9 @@ The SOCI-Ada library offers the following features to the Ada community:
|
||||
|
||||
Currently the following database servers are directly supported via their native interfaces:
|
||||
|
||||
* Oracle
|
||||
* PostgreSQL
|
||||
* MySQL
|
||||
* Oracle
|
||||
* PostgreSQL
|
||||
* MySQL
|
||||
|
||||
Other backends exist in the SOCI Git repository and can be provided with future version of the library.
|
||||
|
||||
@@ -36,4 +36,4 @@ In order to use SOCI-Ada, compile the C++ parts first (core and required backend
|
||||
|
||||
The SOCI-Ada library itself is a single package named `SOCI`. This package can be just imported in the target project as is or pre-built to the binary form if required.
|
||||
|
||||
In order to link the user programs the `-lsoci_core -lstdc++` linker options need to be provided on the Unix/Linux platforms.
|
||||
In order to link the user programs the `-lsoci_core -lstdc++` linker options need to be provided on the Unix/Linux platforms.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SOCI-Ada - manual
|
||||
# SOCI-Ada Bindings - Reference
|
||||
|
||||
## API Reference
|
||||
|
||||
@@ -6,142 +6,147 @@ The SOCI-Ada library is entirely implemented as a single package named `SOCI`. A
|
||||
|
||||
The following describes all publicly visible elements of this package:
|
||||
|
||||
```ada
|
||||
--
|
||||
-- General exception related to database and library usage.
|
||||
--
|
||||
|
||||
--
|
||||
-- General exception related to database and library usage.
|
||||
--
|
||||
|
||||
Database_Error : exception;
|
||||
Database_Error : exception;
|
||||
```
|
||||
|
||||
Each problem related to the interaction with the database or to the incorrect usage of the library itself is signalled by raising this exception. Each occurrence of this exception has some human-readable error message that can be obtained by a call to `Ada.Exceptions.Exception_Message`.
|
||||
|
||||
--
|
||||
-- Session.
|
||||
--
|
||||
```ada
|
||||
--
|
||||
-- Session.
|
||||
--
|
||||
|
||||
type Session is tagged limited private;
|
||||
type Session is tagged limited private;
|
||||
|
||||
not overriding
|
||||
function Make_Session (Connection_String : in String) return Session;
|
||||
not overriding
|
||||
function Make_Session (Connection_String : in String) return Session;
|
||||
|
||||
not overriding
|
||||
procedure Open (This : in out Session; Connection_String : in String);
|
||||
not overriding
|
||||
procedure Open (This : in out Session; Connection_String : in String);
|
||||
|
||||
not overriding
|
||||
procedure Close (This : in out Session);
|
||||
not overriding
|
||||
procedure Close (This : in out Session);
|
||||
|
||||
not overriding
|
||||
function Is_Open (This : in Session) return Boolean;
|
||||
not overriding
|
||||
function Is_Open (This : in Session) return Boolean;
|
||||
```
|
||||
|
||||
The `Session` object can exist in two states: "connected" (or "open") and "disconnected". It can be created as connected at initialization time with a call to the constructor function `Make_Session` or left default-initialized in the disconnected state and later changed to connected with `Open` (the latter option is the only that is available in the Ada 95 version of the library). `Session` objects can be also associated with the connection pool, see below.
|
||||
|
||||
The `Connection_String` should have the form `"backendname://parameters"`, where `backendname` is used to construct the name of the dynamically loadable library that will be used to provide specific database services. Backends included in the current distribution of the main SOCI library are:
|
||||
|
||||
|
||||
* `oracle` (implemented as `libsoci_oracle.so` or `libsoci_oracle.dll`)
|
||||
* `postgresql` (implemented as `libsoci_postgresql.so` or `libsoci_postgresql.dll`)
|
||||
* `mysql` (implemented as `libsoci_mysql.so` or `libsoci_mysql.dll`)
|
||||
|
||||
|
||||
Other backends can be added to the library in the future or by the user himself, please see the documentation of the main SOCI library for details.
|
||||
|
||||
The `parameters` component of the `Connection_String` depends on the given backend, please see the documentation of the main SOCI project for the meaning and recognized options. The web pages related to the backends above are:
|
||||
|
||||
* Oracle: * [http://soci.sourceforge.net/doc/backends/oracle.html](http://soci.sourceforge.net/doc/backends/oracle.html" target="_blank)
|
||||
* PostgreSQL: * [http://soci.sourceforge.net/doc/backends/postgresql.html](http://soci.sourceforge.net/doc/backends/postgresql.html" target="_blank)
|
||||
* MySQL: * [http://soci.sourceforge.net/doc/backends/mysql.html](http://soci.sourceforge.net/doc/backends/mysql.html" target="_blank)
|
||||
* [Oracle](http://soci.sourceforge.net/doc/backends/oracle.html)
|
||||
* [PostgreSQL](http://soci.sourceforge.net/doc/backends/postgresql.html)
|
||||
* [MySQL](http://soci.sourceforge.net/doc/backends/mysql.html)
|
||||
|
||||
The `Open` operation can be called only in the disconnected state (which changes the state of `Session` object to connected). The `Close` operation can be called in any state (provided that the session is not associated with the connection pool, see below) and after that the `Session` is in the disconnected state.
|
||||
|
||||
`Session` objects are closed automatically as part of their finalization. If the `Session` object is associated with the connection pool, the finalizer detaches from the pool without closing the connection.
|
||||
|
||||
```ada
|
||||
-- Transaction management.
|
||||
|
||||
-- Transaction management.
|
||||
not overriding
|
||||
procedure Start (This : in Session);
|
||||
|
||||
not overriding
|
||||
procedure Start (This : in Session);
|
||||
not overriding
|
||||
procedure Commit (This : in Session);
|
||||
|
||||
not overriding
|
||||
procedure Commit (This : in Session);
|
||||
|
||||
not overriding
|
||||
procedure Rollback (This : in Session);
|
||||
not overriding
|
||||
procedure Rollback (This : in Session);
|
||||
```
|
||||
|
||||
These operations handle transactions. The exact meaning of transactions and whether transactions are automatic for some kinds of statements (and which ones) depend on the target database.
|
||||
|
||||
|
||||
-- Immediate query execution.
|
||||
not overriding
|
||||
procedure Execute (This : in Session; Query : in String);
|
||||
```ada
|
||||
-- Immediate query execution.
|
||||
not overriding
|
||||
procedure Execute (This : in Session; Query : in String);
|
||||
```
|
||||
|
||||
This operation allows to create implicit statement, prepare it for the given `Query` and execute it.
|
||||
|
||||
--
|
||||
-- Connection pool management.
|
||||
--
|
||||
```ada
|
||||
--
|
||||
-- Connection pool management.
|
||||
--
|
||||
|
||||
type Connection_Pool (Size : Positive) is tagged limited private;
|
||||
type Connection_Pool (Size : Positive) is tagged limited private;
|
||||
|
||||
not overriding
|
||||
procedure Open
|
||||
(This : in out Connection_Pool;
|
||||
Position : in Positive;
|
||||
Connection_String : in String);
|
||||
not overriding
|
||||
procedure Open
|
||||
(This : in out Connection_Pool;
|
||||
Position : in Positive;
|
||||
Connection_String : in String);
|
||||
|
||||
not overriding
|
||||
procedure Close (This : in out Connection_Pool; Position : in Positive);
|
||||
not overriding
|
||||
procedure Close (This : in out Connection_Pool; Position : in Positive);
|
||||
|
||||
not overriding
|
||||
procedure Lease (This : in out Connection_Pool; S : in out Session'Class);
|
||||
not overriding
|
||||
procedure Lease (This : in out Connection_Pool; S : in out Session'Class);
|
||||
```
|
||||
|
||||
The `Connection_Pool` encapsulates a fixed-size collection of sessions. Individual sessions are indexed from `1` to `Size` (provided as discriminant) and can be `Open`ed and `Close`d explicitly. Each connection in the pool can be created with different `Connection_String`, if needed.
|
||||
|
||||
|
||||
The `Lease` operation allows to associate a given `Session` object (that has to be in the disconnected state itself) with one connection from the pool. The pool guarantees that at most one task can lease a given connection from the pool. If there are no free connections in the pool, the `Lease` operation will block waiting until some connection is freed.
|
||||
|
||||
The `Session` object that is associated with a connection from the pool automatically gives it back to pool as part of the `Session`'s finalizer. There is no other way to "detach" from the pool.
|
||||
|
||||
---
|
||||
#####Note:
|
||||
Note:
|
||||
|
||||
It is assumed that the lifetime of `Connection_Pool` encloses the lifetimes of all `Session` objects that are leased from it. There is no particular protection against it and it is possible to construct a code example with allocators that create partially overlapping `Connection_Pool` and `Session`, but this is considered obscure and not representative to the actual use scenarios. To avoid any potential problems, create `Connection_Pool` in the scope that encloses the scopes of leased `Session`s.
|
||||
---
|
||||
|
||||
--
|
||||
-- Statement.
|
||||
--
|
||||
```ada
|
||||
--
|
||||
-- Statement.
|
||||
--
|
||||
|
||||
type Statement (<>) is tagged limited private;
|
||||
type Statement (<>) is tagged limited private;
|
||||
|
||||
type Data_State is (Data_Null, Data_Not_Null);
|
||||
type Data_State is (Data_Null, Data_Not_Null);
|
||||
|
||||
type Into_Position is private;
|
||||
type Into_Position is private;
|
||||
|
||||
type Vector_Index is new Natural;
|
||||
type Vector_Index is new Natural;
|
||||
```
|
||||
|
||||
The `Statement` type and supporting types. `Data_State` is used to indicate null values in the database sense - each value of into or use elements has a state from this type.
|
||||
|
||||
```ada
|
||||
-- Statement preparation and execution.
|
||||
|
||||
-- Statement preparation and execution.
|
||||
not overriding
|
||||
procedure Prepare (This : in Statement; Query : in String);
|
||||
|
||||
not overriding
|
||||
procedure Prepare (This : in Statement; Query : in String);
|
||||
not overriding
|
||||
procedure Execute
|
||||
(This : in Statement;
|
||||
With_Data_Exchange : in Boolean := False);
|
||||
|
||||
not overriding
|
||||
procedure Execute
|
||||
(This : in Statement;
|
||||
With_Data_Exchange : in Boolean := False);
|
||||
not overriding
|
||||
function Execute
|
||||
(This : in Statement;
|
||||
With_Data_Exchange : in Boolean := False) return Boolean;
|
||||
|
||||
not overriding
|
||||
function Execute
|
||||
(This : in Statement;
|
||||
With_Data_Exchange : in Boolean := False) return Boolean;
|
||||
not overriding
|
||||
function Fetch (This : in Statement) return Boolean;
|
||||
|
||||
not overriding
|
||||
function Fetch (This : in Statement) return Boolean;
|
||||
|
||||
not overriding
|
||||
function Got_Data (This : in Statement) return Boolean;
|
||||
not overriding
|
||||
function Got_Data (This : in Statement) return Boolean;
|
||||
```
|
||||
|
||||
The `Prepare` operation needs to be called before any other operation in the above group and it prepares the execution for the given `Query`. No into and use elements can be created after this operation is called.
|
||||
|
||||
@@ -151,19 +156,20 @@ The `Fetch` function is used to transfer next portion of data (a single row or a
|
||||
|
||||
The `Got_Data` function returns `True` if the last execution or fetch resulted in some data being transmitted from the database server.
|
||||
|
||||
```ada
|
||||
--
|
||||
-- Data items handling.
|
||||
--
|
||||
|
||||
--
|
||||
-- Data items handling.
|
||||
--
|
||||
-- Database-specific types.
|
||||
-- These types are most likely identical to standard Integer,
|
||||
-- Long_Long_Integer and Long_Float, but are defined distinctly
|
||||
-- to avoid interfacing problems with other compilers.
|
||||
|
||||
-- Database-specific types.
|
||||
-- These types are most likely identical to standard Integer,
|
||||
-- Long_Long_Integer and Long_Float, but are defined distinctly
|
||||
-- to avoid interfacing problems with other compilers.
|
||||
|
||||
type DB_Integer is new Interfaces.C.int;
|
||||
type DB_Long_Long_Integer is new Interfaces.Integer_64;
|
||||
type DB_Long_Float is new Interfaces.C.double;
|
||||
type DB_Integer is new Interfaces.C.int;
|
||||
type DB_Long_Long_Integer is new Interfaces.Integer_64;
|
||||
type DB_Long_Float is new Interfaces.C.double;
|
||||
```
|
||||
|
||||
The data types used for interaction with the database are:
|
||||
|
||||
@@ -173,92 +179,88 @@ The data types used for interaction with the database are:
|
||||
* `DB_Long_Float`, defined above
|
||||
* `Ada.Calendar.Time`
|
||||
|
||||
-- Creation of single into elements.
|
||||
```ada
|
||||
-- Creation of single into elements.
|
||||
|
||||
not overriding
|
||||
function Into_String (This : in Statement) return Into_Position;
|
||||
not overriding
|
||||
function Into_String (This : in Statement) return Into_Position;
|
||||
|
||||
not overriding
|
||||
function Into_Integer (This : in Statement) return Into_Position;
|
||||
not overriding
|
||||
function Into_Integer (This : in Statement) return Into_Position;
|
||||
|
||||
not overriding
|
||||
function Into_Long_Long_Integer (This : in Statement) return Into_Position;
|
||||
not overriding
|
||||
function Into_Long_Long_Integer (This : in Statement) return Into_Position;
|
||||
|
||||
not overriding
|
||||
function Into_Long_Float (This : in Statement) return Into_Position;
|
||||
|
||||
not overriding
|
||||
function Into_Time (This : in Statement) return Into_Position;
|
||||
not overriding
|
||||
function Into_Long_Float (This : in Statement) return Into_Position;
|
||||
|
||||
not overriding
|
||||
function Into_Time (This : in Statement) return Into_Position;
|
||||
```
|
||||
|
||||
These functions instruct the library to create internal simple into elements of the relevant type. They return the position of the into element, which can be later used to identify it.
|
||||
|
||||
---
|
||||
#####Note:
|
||||
Note: Simple into elements cannot be created together with vector into elements for the same statement.
|
||||
|
||||
Simple into elements cannot be created together with vector into elements for the same statement.
|
||||
Note: Simple into elements cannot be created together with vector into elements for the same statement.
|
||||
|
||||
---
|
||||
```ada
|
||||
-- Inspection of single into elements.
|
||||
|
||||
---
|
||||
#####Note:
|
||||
Simple into elements cannot be created together with vector into elements for the same statement.
|
||||
not overriding
|
||||
function Get_Into_State
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return Data_State;
|
||||
|
||||
---
|
||||
not overriding
|
||||
function Get_Into_String
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return String;
|
||||
|
||||
-- Inspection of single into elements.
|
||||
not overriding
|
||||
function Get_Into_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return DB_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Into_State
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return Data_State;
|
||||
not overriding
|
||||
function Get_Into_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return DB_Long_Long_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Into_String
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return String;
|
||||
not overriding
|
||||
function Get_Into_Long_Float
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return DB_Long_Float;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return DB_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return DB_Long_Long_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Long_Float
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return DB_Long_Float;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Time
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return Ada.Calendar.Time;
|
||||
not overriding
|
||||
function Get_Into_Time
|
||||
(This : in Statement;
|
||||
Position : in Into_Position)
|
||||
return Ada.Calendar.Time;
|
||||
```
|
||||
|
||||
These functions allow to inspect the state and value of the simple into element identified by its position. If the state of the given element is `Data_Null`, the data-reading functions raise exceptions for that element.
|
||||
|
||||
-- Inspection of vector into elements.
|
||||
```ada
|
||||
-- Inspection of vector into elements.
|
||||
|
||||
not overriding
|
||||
function Get_Into_Vectors_Size (This : in Statement) return Natural;
|
||||
not overriding
|
||||
function Get_Into_Vectors_Size (This : in Statement) return Natural;
|
||||
|
||||
not overriding
|
||||
function Into_Vectors_First_Index (This : in Statement) return Vector_Index;
|
||||
not overriding
|
||||
function Into_Vectors_First_Index (This : in Statement) return Vector_Index;
|
||||
|
||||
not overriding
|
||||
function Into_Vectors_Last_Index (This : in Statement) return Vector_Index;
|
||||
not overriding
|
||||
function Into_Vectors_Last_Index (This : in Statement) return Vector_Index;
|
||||
|
||||
not overriding
|
||||
procedure Into_Vectors_Resize (This : in Statement; New_Size : in Natural);
|
||||
not overriding
|
||||
procedure Into_Vectors_Resize (This : in Statement; New_Size : in Natural);
|
||||
```
|
||||
|
||||
The `Get_Into_Vectors_Size` returns the number of entries in any of the vector into elements for the given statement.
|
||||
|
||||
@@ -266,163 +268,161 @@ The `Into_Vectors_First_Index` returns the lowest index value for vector into el
|
||||
|
||||
The `Into_Vectors_Resize` procedure allows to change the size of all use vectors for the given statement.
|
||||
|
||||
not overriding
|
||||
function Get_Into_Vector_State
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return Data_State;
|
||||
```ada
|
||||
not overriding
|
||||
function Get_Into_Vector_State
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return Data_State;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Vector_String
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return String;
|
||||
not overriding
|
||||
function Get_Into_Vector_String
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return String;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Vector_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return DB_Integer;
|
||||
not overriding
|
||||
function Get_Into_Vector_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return DB_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Vector_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return DB_Long_Long_Integer;
|
||||
not overriding
|
||||
function Get_Into_Vector_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return DB_Long_Long_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Vector_Long_Float
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return DB_Long_Float;
|
||||
not overriding
|
||||
function Get_Into_Vector_Long_Float
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return DB_Long_Float;
|
||||
|
||||
not overriding
|
||||
function Get_Into_Vector_Time
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return Ada.Calendar.Time;
|
||||
not overriding
|
||||
function Get_Into_Vector_Time
|
||||
(This : in Statement;
|
||||
Position : in Into_Position;
|
||||
Index : in Vector_Index)
|
||||
return Ada.Calendar.Time;
|
||||
```
|
||||
|
||||
These functions allow to inspect the state and value of the vector use element identified by its position and index. If the state of the given element is `Data_Null`, the data-reading functions raise exceptions for that element.
|
||||
|
||||
```ada
|
||||
-- Creation of single use elements.
|
||||
|
||||
-- Creation of single use elements.
|
||||
not overriding
|
||||
procedure Use_String (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_String (This : in Statement; Name : in String);
|
||||
not overriding
|
||||
procedure Use_Integer (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Integer (This : in Statement; Name : in String);
|
||||
not overriding
|
||||
procedure Use_Long_Long_Integer (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Long_Long_Integer (This : in Statement; Name : in String);
|
||||
not overriding
|
||||
procedure Use_Long_Float (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Long_Float (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Time (This : in Statement; Name : in String);
|
||||
not overriding
|
||||
procedure Use_Time (This : in Statement; Name : in String);
|
||||
```
|
||||
|
||||
These functions instruct the library to create internal simple use elements of the relevant type, identified by the given `Name`.
|
||||
|
||||
---
|
||||
##### Note:
|
||||
Simple use elements cannot be created together with vector use elements for the same statement.
|
||||
Note:
|
||||
|
||||
Vector use elements cannot be created together with any into elements for the same statement.
|
||||
* Simple use elements cannot be created together with vector use elements for the same statement.
|
||||
* Vector use elements cannot be created together with any into elements for the same statement.
|
||||
|
||||
---
|
||||
```ada
|
||||
-- Creation of vector use elements.
|
||||
|
||||
not overriding
|
||||
procedure Use_Vector_String (This : in Statement; Name : in String);
|
||||
|
||||
-- Creation of vector use elements.
|
||||
not overriding
|
||||
procedure Use_Vector_Integer (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Vector_String (This : in Statement; Name : in String);
|
||||
not overriding
|
||||
procedure Use_Vector_Long_Long_Integer (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Vector_Integer (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Vector_Long_Long_Integer (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Vector_Long_Float (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Vector_Time (This : in Statement; Name : in String);
|
||||
not overriding
|
||||
procedure Use_Vector_Long_Float (This : in Statement; Name : in String);
|
||||
|
||||
not overriding
|
||||
procedure Use_Vector_Time (This : in Statement; Name : in String);
|
||||
```
|
||||
|
||||
These functions instruct the library to create internal vector use elements of the relevant type, identified by the given `Name`.
|
||||
|
||||
Note:
|
||||
|
||||
---
|
||||
#####Note:
|
||||
* Simple use elements cannot be created together with vector use elements for the same statement.
|
||||
* Vector use elements cannot be created together with any into elements for the same statement.
|
||||
|
||||
Simple use elements cannot be created together with vector use elements for the same statement.
|
||||
```ada
|
||||
-- Modifiers for single use elements.
|
||||
|
||||
Vector use elements cannot be created together with any into elements for the same statement.
|
||||
not overriding
|
||||
procedure Set_Use_State
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
State : in Data_State);
|
||||
|
||||
---
|
||||
not overriding
|
||||
procedure Set_Use_String
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in String);
|
||||
|
||||
-- Modifiers for single use elements.
|
||||
not overriding
|
||||
procedure Set_Use_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in DB_Integer);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_State
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
State : in Data_State);
|
||||
not overriding
|
||||
procedure Set_Use_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in DB_Long_Long_Integer);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_String
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in String);
|
||||
not overriding
|
||||
procedure Set_Use_Long_Float
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in DB_Long_Float);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in DB_Integer);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in DB_Long_Long_Integer);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Long_Float
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in DB_Long_Float);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Time
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in Ada.Calendar.Time);
|
||||
not overriding
|
||||
procedure Set_Use_Time
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Value : in Ada.Calendar.Time);
|
||||
```
|
||||
|
||||
These operations allow to modify the state and value of simple use elements. Setting the value of use element automatically sets its state to `Data_Not_Null`.
|
||||
|
||||
```ada
|
||||
-- Modifiers for vector use elements.
|
||||
|
||||
-- Modifiers for vector use elements.
|
||||
not overriding
|
||||
function Get_Use_Vectors_Size (This : in Statement) return Natural;
|
||||
|
||||
not overriding
|
||||
function Get_Use_Vectors_Size (This : in Statement) return Natural;
|
||||
not overriding
|
||||
function Use_Vectors_First_Index (This : in Statement) return Vector_Index;
|
||||
|
||||
not overriding
|
||||
function Use_Vectors_First_Index (This : in Statement) return Vector_Index;
|
||||
not overriding
|
||||
function Use_Vectors_Last_Index (This : in Statement) return Vector_Index;
|
||||
|
||||
not overriding
|
||||
function Use_Vectors_Last_Index (This : in Statement) return Vector_Index;
|
||||
|
||||
not overriding
|
||||
procedure Use_Vectors_Resize (This : in Statement; New_Size : in Natural);
|
||||
not overriding
|
||||
procedure Use_Vectors_Resize (This : in Statement; New_Size : in Natural);
|
||||
```
|
||||
|
||||
The `Get_Use_Vectors_Size` returns the number of entries in any of the vector use elements for the given statement.
|
||||
|
||||
@@ -430,94 +430,95 @@ The `Use_Vectors_First_Index` returns the lowest index value for vector use elem
|
||||
|
||||
The `Use_Vectors_Resize` procedure allows to change the size of all use vectors for the given statement.
|
||||
|
||||
```ada
|
||||
not overriding
|
||||
procedure Set_Use_Vector_State
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
State : in Data_State);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Vector_State
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
State : in Data_State);
|
||||
not overriding
|
||||
procedure Set_Use_Vector_String
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in String);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Vector_String
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in String);
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in DB_Integer);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in DB_Integer);
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in DB_Long_Long_Integer);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in DB_Long_Long_Integer);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Long_Float
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in DB_Long_Float);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Time
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in Ada.Calendar.Time);
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Long_Float
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in DB_Long_Float);
|
||||
|
||||
not overriding
|
||||
procedure Set_Use_Vector_Time
|
||||
(This : in Statement;
|
||||
Name : in String;
|
||||
Index : in Vector_Index;
|
||||
Value : in Ada.Calendar.Time);
|
||||
```
|
||||
|
||||
These operations allow to modify the state and value of vector use elements. Setting the value of use element automatically sets its state to `Data_Not_Null`.
|
||||
|
||||
```ada
|
||||
-- Inspection of single use elements.
|
||||
--
|
||||
-- Note: Use elements can be modified by the database if they
|
||||
-- are bound to out and inout parameters of stored procedures
|
||||
-- (although this is not supported by all database backends).
|
||||
-- This feature is available only for single use elements.
|
||||
|
||||
-- Inspection of single use elements.
|
||||
--
|
||||
-- Note: Use elements can be modified by the database if they
|
||||
-- are bound to out and inout parameters of stored procedures
|
||||
-- (although this is not supported by all database backends).
|
||||
-- This feature is available only for single use elements.
|
||||
not overriding
|
||||
function Get_Use_State
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return Data_State;
|
||||
|
||||
not overriding
|
||||
function Get_Use_State
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return Data_State;
|
||||
not overriding
|
||||
function Get_Use_String
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return String;
|
||||
|
||||
not overriding
|
||||
function Get_Use_String
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return String;
|
||||
not overriding
|
||||
function Get_Use_Integer
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return DB_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Use_Integer
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return DB_Integer;
|
||||
not overriding
|
||||
function Get_Use_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return DB_Long_Long_Integer;
|
||||
|
||||
not overriding
|
||||
function Get_Use_Long_Long_Integer
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return DB_Long_Long_Integer;
|
||||
not overriding
|
||||
function Get_Use_Long_Float
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return DB_Long_Float;
|
||||
|
||||
not overriding
|
||||
function Get_Use_Long_Float
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return DB_Long_Float;
|
||||
not overriding
|
||||
function Get_Use_Time
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return Ada.Calendar.Time;
|
||||
```
|
||||
|
||||
not overriding
|
||||
function Get_Use_Time
|
||||
(This : in Statement;
|
||||
Name : in String)
|
||||
return Ada.Calendar.Time;
|
||||
|
||||
These functions allow to inspect the state and value of the simple use element identified by its name. If the state of the given element is `Data_Null`, the data-reading functions raise exceptions for that element.
|
||||
These functions allow to inspect the state and value of the simple use element identified by its name. If the state of the given element is `Data_Null`, the data-reading functions raise exceptions for that element.
|
||||
|
||||
@@ -4,7 +4,7 @@ The SOCI library is distributed under the terms of the [Boost Software License](
|
||||
|
||||
## Boost Software License
|
||||
|
||||
*Version 1.0 - August 17th, 2003*
|
||||
Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
|
||||
@@ -8,28 +8,28 @@ Depending on the design of the client application this might be also the most st
|
||||
For some applications, however, it might be preferable to decouple the set of threads from the set of sessions, so that they can be optimized separately with different resources in mind.
|
||||
The `connection_pool` class is provided for this purpose:
|
||||
|
||||
```cpp
|
||||
// phase 1: preparation
|
||||
|
||||
// phase 1: preparation
|
||||
const size_t poolSize = 10;
|
||||
connection_pool pool(poolSize);
|
||||
|
||||
const size_t poolSize = 10;
|
||||
connection_pool pool(poolSize);
|
||||
for (size_t i = 0; i != poolSize; ++i)
|
||||
{
|
||||
session & sql = pool.at(i);
|
||||
|
||||
for (size_t i = 0; i != poolSize; ++i)
|
||||
{
|
||||
session & sql = pool.at(i);
|
||||
sql.open("postgresql://dbname=mydb");
|
||||
}
|
||||
|
||||
sql.open("postgresql://dbname=mydb");
|
||||
}
|
||||
// phase 2: usage from working threads
|
||||
|
||||
// phase 2: usage from working threads
|
||||
{
|
||||
session sql(pool);
|
||||
|
||||
{
|
||||
session sql(pool);
|
||||
|
||||
sql << "select something from somewhere...";
|
||||
|
||||
} // session is returned to the pool automatically
|
||||
sql << "select something from somewhere...";
|
||||
|
||||
} // session is returned to the pool automatically
|
||||
```
|
||||
|
||||
The `connection_pool`'s constructor expects the size of the pool and internally creates an array of `session`s in the disconnected state.
|
||||
Later, the `at` function provides *non-synchronized* access to each element of the array.
|
||||
@@ -44,4 +44,4 @@ This way, the connection pool guarantees that its session objects are never used
|
||||
|
||||
Note that the above scheme is the simplest way to use the connection pool, but it is also constraining in the fact that the `session`'s constructor can *block* waiting for the availability of some entry in the pool.
|
||||
For more demanding users there are also low-level functions that allow to lease sessions from the pool with timeout on wait.
|
||||
Please consult the [reference](reference.html) for details.
|
||||
Please consult the [reference](reference.html) for details.
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
|
||||
The `procedure` class provides a convenient mechanism for calling stored procedures:
|
||||
|
||||
sql << "create or replace procedure echo(output out varchar2,"
|
||||
"input in varchar2) as "
|
||||
"begin output := input; end;";
|
||||
|
||||
std::string in("my message");
|
||||
std::string out;
|
||||
procedure proc = (sql.prepare << "echo(:output, :input)",
|
||||
use(out, "output"),
|
||||
use(in, "input"));
|
||||
proc.execute(true);
|
||||
assert(out == "my message");
|
||||
```cpp
|
||||
sql << "create or replace procedure echo(output out varchar2,"
|
||||
"input in varchar2) as "
|
||||
"begin output := input; end;";
|
||||
|
||||
std::string in("my message");
|
||||
std::string out;
|
||||
procedure proc = (sql.prepare << "echo(:output, :input)", use(out, "output"), use(in, "input"));
|
||||
proc.execute(true);
|
||||
assert(out == "my message");
|
||||
```
|
||||
|
||||
## Portability note
|
||||
|
||||
|
||||
@@ -4,20 +4,25 @@
|
||||
|
||||
In many cases, the SQL query is intended to be executed only once, which means that statement parsing and execution can go together. The `session` class provides a special `once` member, which triggers parsing and execution of such one-time statements:
|
||||
|
||||
sql.once << "drop table persons";
|
||||
```cpp
|
||||
sql.once << "drop table persons";
|
||||
```
|
||||
|
||||
For shorter syntax, the following form is also allowed:
|
||||
|
||||
sql << "drop table persons";
|
||||
```cpp
|
||||
sql << "drop table persons";
|
||||
```
|
||||
|
||||
The IOStream-like interface is exactly what it looks like, so that the statement text can be composed of many parts, involving anything that is *streamable* (including custom classes, if they have appropriate `operator<<`):
|
||||
|
||||
```cpp
|
||||
string tableName = "persons";
|
||||
sql << "drop table " << tableName;
|
||||
|
||||
string tableName = "persons";
|
||||
sql << "drop table " << tableName;
|
||||
|
||||
int id = 123;
|
||||
sql << "delete from companies where id = " << id;
|
||||
int id = 123;
|
||||
sql << "delete from companies where id = " << id;
|
||||
```
|
||||
|
||||
## Query transformation
|
||||
|
||||
@@ -31,46 +36,51 @@ For one-time statements, query transformation is performed before each execution
|
||||
|
||||
A few short examples how to use query transformation:
|
||||
|
||||
*defined as free function:*
|
||||
* defined as free function:
|
||||
|
||||
std::string less_than_ten(std::string query)
|
||||
```cpp
|
||||
std::string less_than_ten(std::string query)
|
||||
{
|
||||
return query + " WHERE price < 10";
|
||||
}
|
||||
|
||||
session sql(postgresql, "dbname=mydb");
|
||||
sql.set_query_transformation(less_than_ten);
|
||||
sql << "DELETE FROM item";
|
||||
```
|
||||
|
||||
* defined as function object:
|
||||
|
||||
```cpp
|
||||
struct order : std::unary_function<std::string, std::string>
|
||||
{
|
||||
order(std::string const& by) : by_(by) {}
|
||||
|
||||
result_type operator()(argument_type query) const
|
||||
{
|
||||
return query + " WHERE price < 10";
|
||||
return query + " ORDER BY " + by_;
|
||||
}
|
||||
|
||||
session sql(postgresql, "dbname=mydb");
|
||||
sql.set_query_transformation(less_than_ten);
|
||||
sql << "DELETE FROM item";
|
||||
std::string by_;
|
||||
};
|
||||
|
||||
*defined as function object:*
|
||||
char const* query = "SELECT * FROM product";
|
||||
sql.set_query_transformation(order("price");
|
||||
sql << query;
|
||||
sql.set_query_transformation(order("id");
|
||||
sql << query;
|
||||
```
|
||||
|
||||
struct order : std::unary_function<std::string, std::string>
|
||||
{
|
||||
order(std::string const& by) : by_(by) {}
|
||||
|
||||
result_type operator()(argument_type query) const
|
||||
{
|
||||
return query + " ORDER BY " + by_;
|
||||
}
|
||||
|
||||
std::string by_;
|
||||
};
|
||||
|
||||
char const* query = "SELECT * FROM product";
|
||||
sql.set_query_transformation(order("price");
|
||||
sql << query;
|
||||
sql.set_query_transformation(order("id");
|
||||
sql << query;
|
||||
|
||||
*defined as lambda function (since C++11):*
|
||||
|
||||
std::string dep = "sales";
|
||||
sql.set_query_transformation(
|
||||
[&dep](std::string const& query) {
|
||||
return query + " WHERE department = '" + dep + "'";
|
||||
});
|
||||
sql << "SELECT * FROM employee";
|
||||
* defined as lambda function (since C++11):
|
||||
|
||||
```cpp
|
||||
std::string dep = "sales";
|
||||
sql.set_query_transformation(
|
||||
[&dep](std::string const& query) {
|
||||
return query + " WHERE department = '" + dep + "'";
|
||||
});
|
||||
sql << "SELECT * FROM employee";
|
||||
```
|
||||
|
||||
Query transformations enable users with simple mechanism to apply extra requirements to or interact with SQL statement being executed and that is without changing the SQL statement itself which may be passed from different
|
||||
parts of application.
|
||||
@@ -81,4 +91,4 @@ For example, the query transformation may be used to:
|
||||
* prefix table names with new schema to allow namespaces switch
|
||||
* validate SQL statements
|
||||
* perform sanitization checking for any unverified input
|
||||
* apply database-specific features like add optimization hints to SQL statements (i.e. `SELECT /*+RULE*/ A FROM C` in Oracle 9)
|
||||
* apply database-specific features like add optimization hints to SQL statements (i.e. `SELECT /*+RULE*/ A FROM C` in Oracle 9)
|
||||
|
||||
@@ -2,53 +2,55 @@
|
||||
|
||||
The following (complete!) example is purposedly provided without any explanation.
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-oracle.h"
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
```cpp
|
||||
#include "soci.h"
|
||||
#include "soci-oracle.h"
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
using namespace soci;
|
||||
using namespace std;
|
||||
using namespace soci;
|
||||
using namespace std;
|
||||
|
||||
bool get_name(string &name) {
|
||||
cout << "Enter name: ";
|
||||
return cin >> name;
|
||||
}
|
||||
bool get_name(string &name) {
|
||||
cout << "Enter name: ";
|
||||
return cin >> name;
|
||||
}
|
||||
|
||||
int main()
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
session sql(oracle, "service=mydb user=john password=secret");
|
||||
|
||||
int count;
|
||||
sql << "select count(*) from phonebook", into(count);
|
||||
|
||||
cout << "We have " << count << " entries in the phonebook.\n";
|
||||
|
||||
string name;
|
||||
while (get_name(name))
|
||||
{
|
||||
session sql(oracle, "service=mydb user=john password=secret");
|
||||
string phone;
|
||||
indicator ind;
|
||||
sql << "select phone from phonebook where name = :name",
|
||||
into(phone, ind), use(name);
|
||||
|
||||
int count;
|
||||
sql << "select count(*) from phonebook", into(count);
|
||||
|
||||
cout << "We have " << count << " entries in the phonebook.\n";
|
||||
|
||||
string name;
|
||||
while (get_name(name))
|
||||
if (ind == i_ok)
|
||||
{
|
||||
string phone;
|
||||
indicator ind;
|
||||
sql << "select phone from phonebook where name = :name",
|
||||
into(phone, ind), use(name);
|
||||
|
||||
if (ind == i_ok)
|
||||
{
|
||||
cout << "The phone number is " << phone << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "There is no phone for " << name << '\n';
|
||||
}
|
||||
cout << "The phone number is " << phone << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "There is no phone for " << name << '\n';
|
||||
}
|
||||
}
|
||||
catch (exception const &e)
|
||||
{
|
||||
cerr << "Error: " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
catch (exception const &e)
|
||||
{
|
||||
cerr << "Error: " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -4,17 +4,19 @@
|
||||
|
||||
Consider the following examples:
|
||||
|
||||
// Example 1.
|
||||
for (int i = 0; i != 100; ++i)
|
||||
{
|
||||
sql << "insert into numbers(value) values(" << i << ")";
|
||||
}
|
||||
```cpp
|
||||
// Example 1.
|
||||
for (int i = 0; i != 100; ++i)
|
||||
{
|
||||
sql << "insert into numbers(value) values(" << i << ")";
|
||||
}
|
||||
|
||||
// Example 2.
|
||||
for (int i = 0; i != 100; ++i)
|
||||
{
|
||||
sql << "insert into numbers(value) values(:val)", use(i);
|
||||
}
|
||||
// Example 2.
|
||||
for (int i = 0; i != 100; ++i)
|
||||
{
|
||||
sql << "insert into numbers(value) values(:val)", use(i);
|
||||
}
|
||||
```
|
||||
|
||||
Both examples will populate the table `numbers` with the values from `0` to `99`.
|
||||
|
||||
@@ -26,14 +28,16 @@ In fact, more complicated queries are likely to suffer in terms of lower perform
|
||||
|
||||
The following example uses the class `statement` explicitly, by preparing the statement only once and repeating its execution with changing data (note the use of `prepare` member of `session` class):
|
||||
|
||||
int i;
|
||||
statement st = (sql.prepare <<
|
||||
"insert into numbers(value) values(:val)",
|
||||
use(i));
|
||||
for (i = 0; i != 100; ++i)
|
||||
{
|
||||
st.execute(true);
|
||||
}
|
||||
```cpp
|
||||
int i;
|
||||
statement st = (sql.prepare <<
|
||||
"insert into numbers(value) values(:val)",
|
||||
use(i));
|
||||
for (i = 0; i != 100; ++i)
|
||||
{
|
||||
st.execute(true);
|
||||
}
|
||||
```
|
||||
|
||||
The `true` parameter given to the `execute` method indicates that the actual data exchange is wanted, so that the meaning of the whole example is
|
||||
|
||||
@@ -57,35 +61,41 @@ The `rowset` itself can be used only with select queries.
|
||||
The following example creates an instance of the `rowset` class and binds query results into elements of `int` type - in this query only one result column is expected.
|
||||
After executing the query the code iterates through the query result using `rowset_iterator`:
|
||||
|
||||
rowset<int> rs = (sql.prepare << "select values from numbers");
|
||||
```cpp
|
||||
rowset<int> rs = (sql.prepare << "select values from numbers");
|
||||
|
||||
for (rowset<int>::const_iterator it = rs.begin(); it != rs.end(); ++it)
|
||||
{
|
||||
cout << *it << '\n';
|
||||
}
|
||||
for (rowset<int>::const_iterator it = rs.begin(); it != rs.end(); ++it)
|
||||
{
|
||||
cout << *it << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Another example shows how to retrieve more complex results, where `rowset` elements are of type `row` and therefore use [dynamic bindings](exchange.html#dynamic):
|
||||
|
||||
// person table has 4 columns
|
||||
```cpp
|
||||
// person table has 4 columns
|
||||
|
||||
rowset<row> rs = (sql.prepare << "select id, firstname, lastname, gender from person");
|
||||
rowset<row> rs = (sql.prepare << "select id, firstname, lastname, gender from person");
|
||||
|
||||
// iteration through the resultset:
|
||||
for (rowset<row>::const_iterator it = rs.begin(); it != rs.end(); ++it)
|
||||
{
|
||||
row const& row = *it;
|
||||
// iteration through the resultset:
|
||||
for (rowset<row>::const_iterator it = rs.begin(); it != rs.end(); ++it)
|
||||
{
|
||||
row const& row = *it;
|
||||
|
||||
// dynamic data extraction from each row:
|
||||
cout << "Id: " << row.get<int>(0) << '\n'
|
||||
<< "Name: " << row.get<string>(1) << " " << row.get<string>(2) << '\n'
|
||||
<< "Gender: " << row.get<string>(3) << endl;
|
||||
}
|
||||
// dynamic data extraction from each row:
|
||||
cout << "Id: " << row.get<int>(0) << '\n'
|
||||
<< "Name: " << row.get<string>(1) << " " << row.get<string>(2) << '\n'
|
||||
<< "Gender: " << row.get<string>(3) << endl;
|
||||
}
|
||||
```
|
||||
|
||||
The `rowset_iterator` can be used with standard algorithms as well:
|
||||
|
||||
rowset<string> rs = (sql.prepare << "select firstname from person");
|
||||
```cpp
|
||||
rowset<string> rs = (sql.prepare << "select firstname from person");
|
||||
|
||||
std::copy(rs.begin(), rs.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
|
||||
std::copy(rs.begin(), rs.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
|
||||
```
|
||||
|
||||
Above, the query result contains a single column which is bound to `rowset` element of type of `std::string`.
|
||||
All records are sent to standard output using the `std::copy` algorithm.
|
||||
@@ -99,24 +109,24 @@ The following example presents how to insert 100 records in 4 batches.
|
||||
It is also important to note, that size of vector remains equal in every batch interaction.
|
||||
This ensures vector is not reallocated and, what's crucial for the bulk trick, new data should be pushed to the vector before every call to `statement::execute`:
|
||||
|
||||
// Example 3.
|
||||
void fill_ids(std::vector<int>& ids)
|
||||
{
|
||||
for (std::size_t i = 0; i < ids.size(); ++i)
|
||||
ids[i] = i; // mimics source of a new ID
|
||||
}
|
||||
```cpp
|
||||
// Example 3.
|
||||
void fill_ids(std::vector<int>& ids)
|
||||
{
|
||||
for (std::size_t i = 0; i < ids.size(); ++i)
|
||||
ids[i] = i; // mimics source of a new ID
|
||||
}
|
||||
|
||||
const int BATCH_SIZE = 25;
|
||||
std::vector<int> ids(BATCH_SIZE);
|
||||
const int BATCH_SIZE = 25;
|
||||
std::vector<int> ids(BATCH_SIZE);
|
||||
|
||||
statement st = (sql.prepare <<
|
||||
"insert into numbers(value) values(:val)",
|
||||
use(ids));
|
||||
for (int i = 0; i != 4; ++i)
|
||||
{
|
||||
fill_ids(ids);
|
||||
st.execute(true);
|
||||
}
|
||||
statement st = (sql.prepare << "insert into numbers(value) values(:val)", use(ids));
|
||||
for (int i = 0; i != 4; ++i)
|
||||
{
|
||||
fill_ids(ids);
|
||||
st.execute(true);
|
||||
}
|
||||
```
|
||||
|
||||
Given batch size is 25, this example should insert 4 x 25 = 100 records.
|
||||
|
||||
@@ -124,15 +134,15 @@ Given batch size is 25, this example should insert 4 x 25 = 100 records.
|
||||
|
||||
It is also possible to read all the numbers written in the above examples:
|
||||
|
||||
int i;
|
||||
statement st = (sql.prepare <<
|
||||
"select value from numbers order by value",
|
||||
into(i));
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
{
|
||||
cout << i << '\n';
|
||||
}
|
||||
```cpp
|
||||
int i;
|
||||
statement st = (sql.prepare << "select value from numbers order by value", into(i));
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
{
|
||||
cout << i << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the `execute` method is called with the default parameter `false`.
|
||||
This means that the statement should be executed, but the actual data exchange will be performed later.
|
||||
@@ -144,32 +154,35 @@ The above code example should be treated as an idiomatic way of reading many row
|
||||
|
||||
It is further possible to select records in batches into `std::vector` based types, with the size of the vector specifying the number of records to retrieve in each round trip:
|
||||
|
||||
std::vector<int> valsOut(100);
|
||||
sql << "select val from numbers", into(valsOut);
|
||||
```cpp
|
||||
std::vector<int> valsOut(100);
|
||||
sql << "select val from numbers", into(valsOut);
|
||||
```
|
||||
|
||||
Above, the value `100` indicates that no more values should be retrieved, even if it would be otherwise possible.
|
||||
If there are less rows than asked for, the vector will be appropriately down-sized.
|
||||
|
||||
The `statement::execute()` and `statement::fetch()` functions can also be used to repeatedly select all rows returned by a query into a vector based type:
|
||||
|
||||
|
||||
const int BATCH_SIZE = 30;
|
||||
std::vector<int> valsOut(BATCH_SIZE);
|
||||
statement st = (sql.prepare <<
|
||||
"select value from numbers",
|
||||
into(valsOut));
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
```cpp
|
||||
const int BATCH_SIZE = 30;
|
||||
std::vector<int> valsOut(BATCH_SIZE);
|
||||
statement st = (sql.prepare <<
|
||||
"select value from numbers",
|
||||
into(valsOut));
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
{
|
||||
std::vector<int>::iterator pos;
|
||||
for(pos = valsOut.begin(); pos != valsOut.end(); ++pos)
|
||||
{
|
||||
std::vector<int>::iterator pos;
|
||||
for(pos = valsOut.begin(); pos != valsOut.end(); ++pos)
|
||||
{
|
||||
cout << *pos << '\n';
|
||||
}
|
||||
|
||||
valsOut.resize(BATCH_SIZE);
|
||||
cout << *pos << '\n';
|
||||
}
|
||||
|
||||
valsOut.resize(BATCH_SIZE);
|
||||
}
|
||||
```
|
||||
|
||||
Assuming there are 100 rows returned by the query, the above code will retrieve and print all of them.
|
||||
Since the output vector was created with size 30, it will take (at least) 4 calls to `fetch()` to retrieve all 100 values.
|
||||
Each call to `fetch()` can potentially resize the vector to a size less than its initial size - how often this happens depends on the underlying database implementation.
|
||||
@@ -189,47 +202,48 @@ Actually, all supported backends guarantee that the requested number of rows wil
|
||||
This means that the manual vector resizing is in practice not needed - the vector will keep its size until the end of rowset.
|
||||
The above idiom, however, is provided with future backends in mind, where the constant size of the vector might be too expensive to guarantee and where allowing `fetch` to down-size the vector even before reaching the end of rowset might buy some performance gains.
|
||||
|
||||
|
||||
## Statement caching
|
||||
|
||||
Some backends have some facilities to improve statement parsing and compilation to limit overhead when creating commonly used query.
|
||||
But for backends that does not support this kind optimization you can keep prepared statement and use it later with new references.
|
||||
To do such, prepare a statement as usual, you have to use `exchange` to bind new variables to statement object, then `execute` statement and finish by cleaning bound references with `bind_clean_up`.
|
||||
|
||||
sql << "CREATE TABLE test(a INTEGER)";
|
||||
```cpp
|
||||
sql << "CREATE TABLE test(a INTEGER)";
|
||||
|
||||
{
|
||||
// prepare statement
|
||||
soci::statement stmt = (db.prepare << "INSERT INTO numbers(value) VALUES(:val)");
|
||||
|
||||
{
|
||||
// prepare statement
|
||||
soci::statement stmt = (db.prepare << "INSERT INTO numbers(value) VALUES(:val)");
|
||||
// first insert
|
||||
int a0 = 0;
|
||||
|
||||
{
|
||||
// first insert
|
||||
int a0 = 0;
|
||||
// update reference
|
||||
stmt.exchange(soci::use(a0));
|
||||
|
||||
// update reference
|
||||
stmt.exchange(soci::use(a0));
|
||||
|
||||
stmt.define_and_bind();
|
||||
stmt.execute(true);
|
||||
stmt.bind_clean_up();
|
||||
}
|
||||
|
||||
{
|
||||
// come later, second insert
|
||||
int a1 = 1;
|
||||
|
||||
// update reference
|
||||
stmt.exchange(soci::use(a1));
|
||||
|
||||
stmt.define_and_bind();
|
||||
stmt.execute(true);
|
||||
stmt.bind_clean_up();
|
||||
}
|
||||
stmt.define_and_bind();
|
||||
stmt.execute(true);
|
||||
stmt.bind_clean_up();
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> v(10);
|
||||
db << "SELECT value FROM numbers", soci::into(v);
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
std::cout << "value " << i << ": " << v[i] << std::endl;
|
||||
// come later, second insert
|
||||
int a1 = 1;
|
||||
|
||||
// update reference
|
||||
stmt.exchange(soci::use(a1));
|
||||
|
||||
stmt.define_and_bind();
|
||||
stmt.execute(true);
|
||||
stmt.bind_clean_up();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> v(10);
|
||||
db << "SELECT value FROM numbers", soci::into(v);
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
std::cout << "value " << i << ": " << v[i] << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -15,12 +15,13 @@ The core part of the library and the backend interface definition are placed in
|
||||
|
||||
Everything in SOCI is declared in the namespace `soci`. All code examples presented in this documentation assume that your code begins with something like:
|
||||
|
||||
```cpp
|
||||
#include "soci.h"
|
||||
// other includes if necessary
|
||||
|
||||
#include "soci.h"
|
||||
// other includes if necessary
|
||||
using namespace soci;
|
||||
|
||||
using namespace soci;
|
||||
// ...
|
||||
```
|
||||
|
||||
// ...
|
||||
|
||||
*Note:* In simple programs, `#include` for the relevant backend is needed only in the file where the `session` object is created with explicit name of the backend factory. The example program on the [previous page](index.html) shows the appropriate `#include` directive for the Oracle backend. It is also possible to name backends at run-time as part of the connection string, in which case no backend-specific `#include` directive is necessary.
|
||||
Note: In simple programs, `#include` for the relevant backend is needed only in the file where the `session` object is created with explicit name of the backend factory. The example program on the [previous page](index.html) shows the appropriate `#include` directive for the Oracle backend. It is also possible to name backends at run-time as part of the connection string, in which case no backend-specific `#include` directive is necessary.
|
||||
|
||||
@@ -8,34 +8,38 @@ The SOCI library provides the following members of the `session` class for trans
|
||||
|
||||
In addition to the above there is a RAII wrapper that allows to associate the transaction with the given scope of code:
|
||||
|
||||
class transaction
|
||||
{
|
||||
public:
|
||||
explicit transaction(session & sql);
|
||||
```cpp
|
||||
class transaction
|
||||
{
|
||||
public:
|
||||
explicit transaction(session & sql);
|
||||
|
||||
~transaction();
|
||||
~transaction();
|
||||
|
||||
void commit();
|
||||
void rollback();
|
||||
void commit();
|
||||
void rollback();
|
||||
|
||||
private:
|
||||
// ...
|
||||
};
|
||||
private:
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
The object of class `transaction` will roll back automatically when the object is destroyed
|
||||
(usually as a result of leaving the scope) *and* when the transaction was not explicitly committed before that.
|
||||
|
||||
A typical usage pattern for this class might be:
|
||||
|
||||
{
|
||||
transaction tr(sql);
|
||||
```cpp
|
||||
{
|
||||
transaction tr(sql);
|
||||
|
||||
sql << "insert into ...";
|
||||
sql << "more sql queries ...";
|
||||
// ...
|
||||
sql << "insert into ...";
|
||||
sql << "more sql queries ...";
|
||||
// ...
|
||||
|
||||
tr.commit();
|
||||
}
|
||||
tr.commit();
|
||||
}
|
||||
```
|
||||
|
||||
With the above pattern the transaction is committed only when the code successfully reaches the end of block.
|
||||
If some exception is thrown before that, the scope will be left without reaching the final statement and the transaction object will automatically roll back in its destructor.
|
||||
|
||||
284
docs/types.md
284
docs/types.md
@@ -45,43 +45,45 @@ Once the data type for each column is known, the data can be formatted appropria
|
||||
|
||||
For example, the code below creates an XML document from a selected row of data from an arbitrary table:
|
||||
|
||||
row r;
|
||||
sql << "select * from some_table", into(r);
|
||||
```cpp
|
||||
row r;
|
||||
sql << "select * from some_table", into(r);
|
||||
|
||||
std::ostringstream doc;
|
||||
doc << "<row>" << std::endl;
|
||||
for(std::size_t i = 0; i != r.size(); ++i)
|
||||
std::ostringstream doc;
|
||||
doc << "<row>" << std::endl;
|
||||
for(std::size_t i = 0; i != r.size(); ++i)
|
||||
{
|
||||
const column_properties & props = r.get_properties(i);
|
||||
|
||||
doc << '<' << props.get_name() << '>';
|
||||
|
||||
switch(props.get_data_type())
|
||||
{
|
||||
const column_properties & props = r.get_properties(i);
|
||||
|
||||
doc << '<' << props.get_name() << '>';
|
||||
|
||||
switch(props.get_data_type())
|
||||
{
|
||||
case dt_string:
|
||||
doc << r.get<std::string>(i);
|
||||
break;
|
||||
case dt_double:
|
||||
doc << r.get<double>(i);
|
||||
break;
|
||||
case dt_integer:
|
||||
doc << r.get<int>(i);
|
||||
break;
|
||||
case dt_long_long:
|
||||
doc << r.get<long long>(i);
|
||||
break;
|
||||
case dt_unsigned_long_long:
|
||||
doc << r.get<unsigned long long>(i);
|
||||
break;
|
||||
case dt_date:
|
||||
std::tm when = r.get<std::tm>(i);
|
||||
doc << asctime(&when);
|
||||
break;
|
||||
}
|
||||
|
||||
doc << "</" << props.get_name() << '>' << std::endl;
|
||||
case dt_string:
|
||||
doc << r.get<std::string>(i);
|
||||
break;
|
||||
case dt_double:
|
||||
doc << r.get<double>(i);
|
||||
break;
|
||||
case dt_integer:
|
||||
doc << r.get<int>(i);
|
||||
break;
|
||||
case dt_long_long:
|
||||
doc << r.get<long long>(i);
|
||||
break;
|
||||
case dt_unsigned_long_long:
|
||||
doc << r.get<unsigned long long>(i);
|
||||
break;
|
||||
case dt_date:
|
||||
std::tm when = r.get<std::tm>(i);
|
||||
doc << asctime(&when);
|
||||
break;
|
||||
}
|
||||
doc << "</row>";
|
||||
|
||||
doc << "</" << props.get_name() << '>' << std::endl;
|
||||
}
|
||||
doc << "</row>";
|
||||
```
|
||||
|
||||
The type `T` parameter that should be passed to `row::get<T>()` depends on the SOCI data type that is returned from `column_properties::get_data_type()`.
|
||||
|
||||
@@ -101,22 +103,26 @@ See the [backend documentation](backends/index.html) for details.
|
||||
|
||||
The `row` also provides access to indicators for each column:
|
||||
|
||||
row r;
|
||||
sql << "select name from some_table where id = 1", into(r);
|
||||
if (r.get_indicator(0) != soci::i_null)
|
||||
{
|
||||
std::cout << r.get<std::string>(0);
|
||||
}
|
||||
```cpp
|
||||
row r;
|
||||
sql << "select name from some_table where id = 1", into(r);
|
||||
if (r.get_indicator(0) != soci::i_null)
|
||||
{
|
||||
std::cout << r.get<std::string>(0);
|
||||
}
|
||||
```
|
||||
|
||||
It is also possible to extract data from the `row` object using its stream-like interface, where each extracted variable should have matching type respective to its position in the chain:
|
||||
|
||||
row r;
|
||||
sql << "select name, address, age from persons where id = 123", into(r);
|
||||
```cpp
|
||||
row r;
|
||||
sql << "select name, address, age from persons where id = 123", into(r);
|
||||
|
||||
string name, address;
|
||||
int age;
|
||||
string name, address;
|
||||
int age;
|
||||
|
||||
r >> name >> address >> age;
|
||||
r >> name >> address >> age;
|
||||
```
|
||||
|
||||
Note, however, that this interface is *not* compatible with the standard `std::istream` class and that it is only possible to extract a single row at a time - for "safety" reasons the row boundary is preserved and it is necessary to perform the `fetch` operation explicitly for each consecutive row.
|
||||
|
||||
@@ -144,51 +150,55 @@ Note that no database-specific code is required to define user conversion.
|
||||
|
||||
The following example shows how the user can extend SOCI to support his own type `MyInt`, which here is some wrapper for the fundamental `int` type:
|
||||
|
||||
class MyInt
|
||||
```cpp
|
||||
class MyInt
|
||||
{
|
||||
public:
|
||||
MyInt() {}
|
||||
MyInt(int i) : i_(i) {}
|
||||
|
||||
void set(int i) { i_ = i; }
|
||||
int get() const { return i_; }
|
||||
|
||||
private:
|
||||
int i_;
|
||||
};
|
||||
|
||||
namespace soci
|
||||
{
|
||||
template <<
|
||||
struct type_conversion<MyInt>
|
||||
{
|
||||
public:
|
||||
MyInt() {}
|
||||
MyInt(int i) : i_(i) {}
|
||||
typedef int base_type;
|
||||
|
||||
void set(int i) { i_ = i; }
|
||||
int get() const { return i_; }
|
||||
|
||||
private:
|
||||
int i_;
|
||||
};
|
||||
|
||||
namespace soci
|
||||
{
|
||||
template <<
|
||||
struct type_conversion<MyInt>
|
||||
static void from_base(int i, indicator ind, MyInt & mi)
|
||||
{
|
||||
typedef int base_type;
|
||||
|
||||
static void from_base(int i, indicator ind, MyInt & mi)
|
||||
if (ind == i_null)
|
||||
{
|
||||
if (ind == i_null)
|
||||
{
|
||||
throw soci_error("Null value not allowed for this type");
|
||||
}
|
||||
|
||||
mi.set(i);
|
||||
throw soci_error("Null value not allowed for this type");
|
||||
}
|
||||
|
||||
static void to_base(const MyInt & mi, int & i, indicator & ind)
|
||||
{
|
||||
i = mi.get();
|
||||
ind = i_ok;
|
||||
}
|
||||
};
|
||||
}
|
||||
mi.set(i);
|
||||
}
|
||||
|
||||
static void to_base(const MyInt & mi, int & i, indicator & ind)
|
||||
{
|
||||
i = mi.get();
|
||||
ind = i_ok;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The above specialization for `soci::type_conversion<MyInt>` is enough to enable the following:
|
||||
|
||||
MyInt i;
|
||||
```cpp
|
||||
MyInt i;
|
||||
|
||||
sql << "select count(*) from person", into(i);
|
||||
sql << "select count(*) from person", into(i);
|
||||
|
||||
cout << "We have " << i.get() << " persons in the database.\n";
|
||||
cout << "We have " << i.get() << " persons in the database.\n";
|
||||
```
|
||||
|
||||
Note that there is a number of types from the Boost library integrated with SOCI out of the box, see [Integration with Boost](boost.html) for complete description. Use these as examples of conversions for more complext data types.
|
||||
|
||||
@@ -206,73 +216,77 @@ For example, the following code maps a `Person` object to and from a database ta
|
||||
|
||||
Note that the mapping is non-invasive - the `Person` object itself does not contain any SOCI-specific code:
|
||||
|
||||
struct Person
|
||||
{
|
||||
int id;
|
||||
std::string firstName;
|
||||
std::string lastName;
|
||||
std::string gender;
|
||||
};
|
||||
```cpp
|
||||
struct Person
|
||||
{
|
||||
int id;
|
||||
std::string firstName;
|
||||
std::string lastName;
|
||||
std::string gender;
|
||||
};
|
||||
|
||||
namespace soci
|
||||
namespace soci
|
||||
{
|
||||
template<>
|
||||
struct type_conversion<Person>
|
||||
{
|
||||
template<>
|
||||
struct type_conversion<Person>
|
||||
typedef values base_type;
|
||||
|
||||
static void from_base(values const & v, indicator /* ind */, Person & p)
|
||||
{
|
||||
typedef values base_type;
|
||||
p.id = v.get<int>("ID");
|
||||
p.firstName = v.get<std::string>("FIRST_NAME");
|
||||
p.lastName = v.get<std::string>("LAST_NAME");
|
||||
|
||||
static void from_base(values const & v, indicator /* ind */, Person & p)
|
||||
{
|
||||
p.id = v.get<int>("ID");
|
||||
p.firstName = v.get<std::string>("FIRST_NAME");
|
||||
p.lastName = v.get<std::string>("LAST_NAME");
|
||||
// p.gender will be set to the default value "unknown"
|
||||
// when the column is null:
|
||||
p.gender = v.get<std::string>("GENDER", "unknown");
|
||||
|
||||
// p.gender will be set to the default value "unknown"
|
||||
// when the column is null:
|
||||
p.gender = v.get<std::string>("GENDER", "unknown");
|
||||
// alternatively, the indicator can be tested directly:
|
||||
// if (v.indicator("GENDER") == i_null)
|
||||
// {
|
||||
// p.gender = "unknown";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// p.gender = v.get<std::string>("GENDER");
|
||||
// }
|
||||
}
|
||||
|
||||
// alternatively, the indicator can be tested directly:
|
||||
// if (v.indicator("GENDER") == i_null)
|
||||
// {
|
||||
// p.gender = "unknown";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// p.gender = v.get<std::string>("GENDER");
|
||||
// }
|
||||
}
|
||||
|
||||
static void to_base(const Person & p, values & v, indicator & ind)
|
||||
{
|
||||
v.set("ID", p.id);
|
||||
v.set("FIRST_NAME", p.firstName);
|
||||
v.set("LAST_NAME", p.lastName);
|
||||
v.set("GENDER", p.gender, p.gender.empty() ? i_null : i_ok);
|
||||
ind = i_ok;
|
||||
}
|
||||
};
|
||||
}
|
||||
static void to_base(const Person & p, values & v, indicator & ind)
|
||||
{
|
||||
v.set("ID", p.id);
|
||||
v.set("FIRST_NAME", p.firstName);
|
||||
v.set("LAST_NAME", p.lastName);
|
||||
v.set("GENDER", p.gender, p.gender.empty() ? i_null : i_ok);
|
||||
ind = i_ok;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
With the above `type_conversion` specialization in place, it is possible to use `Person` directly with SOCI:
|
||||
|
||||
session sql(oracle, "service=db1 user=scott password=tiger");
|
||||
```cpp
|
||||
session sql(oracle, "service=db1 user=scott password=tiger");
|
||||
|
||||
Person p;
|
||||
p.id = 1;
|
||||
p.lastName = "Smith";
|
||||
p.firstName = "Pat";
|
||||
sql << "insert into person(id, first_name, last_name) "
|
||||
"values(:ID, :FIRST_NAME, :LAST_NAME)", use(p);
|
||||
Person p;
|
||||
p.id = 1;
|
||||
p.lastName = "Smith";
|
||||
p.firstName = "Pat";
|
||||
sql << "insert into person(id, first_name, last_name) "
|
||||
"values(:ID, :FIRST_NAME, :LAST_NAME)", use(p);
|
||||
|
||||
Person p1;
|
||||
sql << "select * from person", into(p1);
|
||||
assert(p1.id == 1);
|
||||
assert(p1.firstName + p.lastName == "PatSmith");
|
||||
assert(p1.gender == "unknown");
|
||||
Person p1;
|
||||
sql << "select * from person", into(p1);
|
||||
assert(p1.id == 1);
|
||||
assert(p1.firstName + p.lastName == "PatSmith");
|
||||
assert(p1.gender == "unknown");
|
||||
|
||||
p.firstName = "Patricia";
|
||||
sql << "update person set first_name = :FIRST_NAME "
|
||||
"where id = :ID", use(p);
|
||||
p.firstName = "Patricia";
|
||||
sql << "update person set first_name = :FIRST_NAME "
|
||||
"where id = :ID", use(p);
|
||||
```
|
||||
|
||||
**Note:** The `values` class is currently not suited for use outside of `type_conversion`specializations.
|
||||
Note: The `values` class is currently not suited for use outside of `type_conversion`specializations.
|
||||
It is specially designed to facilitate object-relational mapping when used as shown above.
|
||||
|
||||
@@ -8,67 +8,87 @@ SOCI supports some basic methods to construct portable DDL queries. That is, ins
|
||||
|
||||
It is possible to create a new table in a single statement:
|
||||
|
||||
sql.create_table("t1").column("i", soci::dt_integer).column("j", soci::dt_integer);
|
||||
```cpp
|
||||
sql.create_table("t1").column("i", soci::dt_integer).column("j", soci::dt_integer);
|
||||
```
|
||||
|
||||
Above, table "t1" will be created with two columns ("i", "j") of type integer.
|
||||
|
||||
It is also possible to build similar statements piece by piece, which is useful if the table structure is computed dynamically:
|
||||
|
||||
{
|
||||
soci::ddl_type ddl = sql.create_table("t2");
|
||||
ddl.column("i", soci::dt_integer);
|
||||
ddl.column("j", soci::dt_integer);
|
||||
ddl.column("k", soci::dt_integer)("not null");
|
||||
ddl.primary_key("t2_pk", "j");
|
||||
}
|
||||
```cpp
|
||||
{
|
||||
soci::ddl_type ddl = sql.create_table("t2");
|
||||
ddl.column("i", soci::dt_integer);
|
||||
ddl.column("j", soci::dt_integer);
|
||||
ddl.column("k", soci::dt_integer)("not null");
|
||||
ddl.primary_key("t2_pk", "j");
|
||||
}
|
||||
```
|
||||
|
||||
The actual statement is executed at the end of above block, when the ddl object goes out of scope. The "not null" constraint was added to the definition of column "k" explicitly and in fact any piece of SQL can be inserted this way - with the obvious caveat of having limited portability (the "not null" piece seems to be universaly portable).
|
||||
|
||||
Columns can be added to and dropped from already existing tables as well:
|
||||
|
||||
sql.add_column("t1", "k", soci::dt_integer);
|
||||
// or with constraint:
|
||||
//sql.add_column("t1", "k", soci::dt_integer)("not null");
|
||||
|
||||
sql.drop_column("t1", "i");
|
||||
```cpp
|
||||
sql.add_column("t1", "k", soci::dt_integer);
|
||||
// or with constraint:
|
||||
//sql.add_column("t1", "k", soci::dt_integer)("not null");
|
||||
|
||||
sql.drop_column("t1", "i");
|
||||
```
|
||||
|
||||
If needed, precision and scale can be defined with additional integer arguments to functions that create columns:
|
||||
|
||||
sql.add_column("t1", "s", soci::dt_string, precision);
|
||||
sql.add_column("t1", "d", soci::dt_double, precision, scale);
|
||||
```cpp
|
||||
sql.add_column("t1", "s", soci::dt_string, precision);
|
||||
sql.add_column("t1", "d", soci::dt_double, precision, scale);
|
||||
```
|
||||
|
||||
Tables with foreign keys to each other can be also created:
|
||||
|
||||
{
|
||||
soci::ddl_type ddl = sql.create_table("t3");
|
||||
ddl.column("x", soci::dt_integer);
|
||||
ddl.column("y", soci::dt_integer);
|
||||
ddl.foreign_key("t3_fk", "x", "t2", "j");
|
||||
}
|
||||
```cpp
|
||||
{
|
||||
soci::ddl_type ddl = sql.create_table("t3");
|
||||
ddl.column("x", soci::dt_integer);
|
||||
ddl.column("y", soci::dt_integer);
|
||||
ddl.foreign_key("t3_fk", "x", "t2", "j");
|
||||
}
|
||||
```
|
||||
|
||||
Tables can be dropped, too:
|
||||
|
||||
sql.drop_table("t1");
|
||||
sql.drop_table("t3");
|
||||
sql.drop_table("t2");
|
||||
```cpp
|
||||
sql.drop_table("t1");
|
||||
sql.drop_table("t3");
|
||||
sql.drop_table("t2");
|
||||
```
|
||||
|
||||
Note that due to the differences in the set of types that are actually supported on the target database server, the type mappings, as well as precision and scales, might be different, even in the way that makes them impossible to portably recover with metadata queries.
|
||||
|
||||
In the category of portability utilities, the following functions are also available:
|
||||
|
||||
sql.empty_blob()
|
||||
```cpp
|
||||
sql.empty_blob()
|
||||
```
|
||||
|
||||
the above call returns the string containing expression that represents an empty BLOB value in the given target backend. This expression can be used as part of a bigger SQL statement, for example:
|
||||
|
||||
sql << "insert into my_table (x) values (" + sql.empty_blob() + ")";
|
||||
```cpp
|
||||
sql << "insert into my_table (x) values (" + sql.empty_blob() + ")";
|
||||
```
|
||||
|
||||
and:
|
||||
|
||||
sql.nvl()
|
||||
```cpp
|
||||
sql.nvl()
|
||||
```
|
||||
|
||||
the above call returns the string containing the name of the SQL function that implements the NVL or COALESCE operation in the given target backend, for example:
|
||||
|
||||
sql << "select name, " + sql.nvl() + "(phone, \'UNKNOWN\') from phone_book";
|
||||
```cpp
|
||||
sql << "select name, " + sql.nvl() + "(phone, \'UNKNOWN\') from phone_book";
|
||||
```
|
||||
|
||||
Note: `empty_blob` and `nvl` are implemented in Oracle, PostgreSQL and SQLite3 backends; for other backends their behaviour is as for PostgreSQL.
|
||||
|
||||
@@ -81,10 +101,11 @@ omitting the from clause in this case, others -- e.g. Oracle -- still require
|
||||
providing some syntactically valid from clause even if it is not used. To use
|
||||
this function, simply append the result of this function to the statement:
|
||||
|
||||
double databasePi;
|
||||
session << ("select 4*atan(1)" + session.get_dummy_from_clause()),
|
||||
into(databasePi);
|
||||
|
||||
```cpp
|
||||
double databasePi;
|
||||
session << ("select 4*atan(1)" + session.get_dummy_from_clause()),
|
||||
into(databasePi);
|
||||
```
|
||||
|
||||
If just the name of the dummy table is needed, and not the full clause, you can
|
||||
use `get_dummy_from_table()` to obtain it.
|
||||
@@ -98,27 +119,33 @@ It is possible to portably query the database server to obtain basic metadata in
|
||||
|
||||
In order to get the list of table names in the current schema:
|
||||
|
||||
std::vector<std::string> names(100);
|
||||
sql.get_table_names(), into(names);
|
||||
```cpp
|
||||
std::vector<std::string> names(100);
|
||||
sql.get_table_names(), into(names);
|
||||
```
|
||||
|
||||
alternatively:
|
||||
|
||||
std::string name;
|
||||
soci::statement st = (sql.prepare_table_names(), into(name));
|
||||
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```cpp
|
||||
std::string name;
|
||||
soci::statement st = (sql.prepare_table_names(), into(name));
|
||||
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, to get the description of all columns in the given table:
|
||||
|
||||
soci::column_info ci;
|
||||
soci::statement st = (sql.prepare_column_descriptions(table_name), into(ci));
|
||||
```cpp
|
||||
soci::column_info ci;
|
||||
soci::statement st = (sql.prepare_column_descriptions(table_name), into(ci));
|
||||
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
{
|
||||
// ci fields describe each column in turn
|
||||
}
|
||||
st.execute();
|
||||
while (st.fetch())
|
||||
{
|
||||
// ci fields describe each column in turn
|
||||
}
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@ virtual environments for **hassle-free** SOCI development.
|
||||
* during provision, automatically clones and builds SOCI from `master` branch.
|
||||
* `db2.vm`:
|
||||
* hostname: `vmdb2.local`
|
||||
* IBM DB2 Express-C 9.7 installed from http://archive.canonical.com packages.
|
||||
* IBM DB2 Express-C 9.7 installed from [archive.canonical.com](http://archive.canonical.com) packages.
|
||||
* `oracle.vm`:
|
||||
* *TODO*: provision with Oracle XE
|
||||
* SOCI local git repository (aka `$SOCI_HOME`) is automatically shared on host
|
||||
@@ -35,63 +35,72 @@ it is provisioned with complete DB2 CLI client (libraries and headers).
|
||||
You need to download "IBM Data Server Driver Package (DS Driver)" manually
|
||||
and make it visible to Vagrant:
|
||||
|
||||
1. Go to http://www-01.ibm.com/support/docview.wss?uid=swg21385217
|
||||
2. Download "IBM Data Server Driver Package (DS Driver)"
|
||||
3. Copy the package to `${SOCI_HOME}/tmp` directory, on host machine.
|
||||
1. Go to [IBM Data Server Client Packages](http://www-01.ibm.com/support/docview.wss?uid=swg21385217).
|
||||
2. Download "IBM Data Server Driver Package (DS Driver)".
|
||||
3. Copy the package to `${SOCI_HOME}/tmp` directory, on host machine.
|
||||
|
||||
## Usage
|
||||
|
||||
Below, simple and easy development workflow with Vagrant is outlined:
|
||||
|
||||
* [Boot](https://docs.vagrantup.com/v2/getting-started/up.html)
|
||||
```
|
||||
|
||||
```console
|
||||
vagrant up
|
||||
```
|
||||
|
||||
or boot VMs selectively:
|
||||
```
|
||||
|
||||
```console
|
||||
vagrant up {soci|db2}
|
||||
```
|
||||
|
||||
First time you run it, be patient as Vagrant downloads VM box and
|
||||
provisions it installing all the necessary packages.
|
||||
|
||||
* You can SSH into the machine
|
||||
```
|
||||
|
||||
```console
|
||||
vagrant ssh {soci|db2}
|
||||
```
|
||||
|
||||
* Develop
|
||||
* Run git commands can either from host or VM `soci` (once connected via SSH)
|
||||
```
|
||||
* Run git commands can either from host or VM `soci` (once connected via SSH)
|
||||
|
||||
```console
|
||||
cd /vagrant # aka $SOCI_HOME
|
||||
git pull origin master
|
||||
```
|
||||
* You can edit source code on both, on host or VM `soci`.
|
||||
* For example, edit in your favourite editor on host machine, then build,
|
||||
run, test and debug on guest machine from command line.
|
||||
* Alternatively, edit and build on host machine using your favourite IDE,
|
||||
then test and debug connecting to DBs on guest machines via network.
|
||||
|
||||
* You can edit source code on both, on host or VM `soci`.
|
||||
* For example, edit in your favourite editor on host machine, then build, run, test and debug on guest machine from command line.
|
||||
* Alternatively, edit and build on host machine using your favourite IDE, then test and debug connecting to DBs on guest machines via network.
|
||||
|
||||
* Build on VM `soci`
|
||||
```
|
||||
|
||||
```console
|
||||
vagrant ssh soci
|
||||
cd /vagrant # aka $SOCI_HOME
|
||||
cd soci-build # aka $SOCI_BUILD
|
||||
make
|
||||
```
|
||||
|
||||
You can also execute the `build.h` script provided to run CMake and make
|
||||
```
|
||||
|
||||
```console
|
||||
vagrant ssh soci
|
||||
cd $SOCI_BUILD
|
||||
/vagrant/scripts/vagrant/build.sh
|
||||
```
|
||||
* Debug, only on VM `soci` with gdb.
|
||||
|
||||
* Debug, only on VM `soci`, for example, with gdb or remotely Visual Studio 2017.
|
||||
|
||||
* [Teardown](https://docs.vagrantup.com/v2/getting-started/teardown.html)
|
||||
```
|
||||
|
||||
```console
|
||||
vagrant {suspend|halt|destroy} {soci|db2}
|
||||
```
|
||||
|
||||
Check Vagrant [command-line interface](https://docs.vagrantup.com/v2/cli/index.html)
|
||||
for complete list of commands.
|
||||
Check Vagrant [command-line interface](https://docs.vagrantup.com/v2/cli/index.html) for complete list of commands.
|
||||
|
||||
### Environment variables
|
||||
|
||||
@@ -112,7 +121,5 @@ Note, those variables are also used by provision scripts to set up databases.
|
||||
## Troubleshooting
|
||||
|
||||
* Analyze `vagrant up` output.
|
||||
* On Windows, prefer `vagrant ssh` from inside MinGW Shell where `ssh.exe` is available or
|
||||
learn how to use Vagrant with PuTTY.
|
||||
* If you modify any of `scripts/vagrant/*.sh` scripts, **ensure** they have unified
|
||||
end-of-line characters to `LF` only. Otherwise, provisioning steps may fail.
|
||||
* On Windows, prefer `vagrant ssh` from inside MinGW Shell where `ssh.exe` is available or learn how to use Vagrant with PuTTY.
|
||||
* If you modify any of `scripts/vagrant/*.sh` scripts, **ensure** they have unified end-of-line characters to `LF` only. Otherwise, provisioning steps may fail.
|
||||
|
||||
Reference in New Issue
Block a user