From 9708a1260720d879d76a10f894925962f20611bc Mon Sep 17 00:00:00 2001
From: Vinnie Falco This part of the documentation is provided for those who want to
+write (and contribute!) their own backends. It is anyway recommended
+that authors of new backend see the code of some existing backend for
+hints on how things are really done. The backend interface is a set of base classes that the actual backends
+are supposed to specialize. The main SOCI interface uses only the
+interface and respecting the protocol (for example, the order of
+function calls) described here. Note that both the interface and the
+protocol were initially designed with the Oracle database in mind,
+which means
+that whereas it is quite natural with respect to the way Oracle API
+(OCI) works, it might impose some implementation burden on other
+backends, where things are done differently and therefore have to be
+adjusted, cached, converted, etc. The interface to the common SOCI interface is defined in the All names are defined in either The The The The The The The intended use of The The The The intended use for Objects of this type (or rather of type derived from this one) are used
+to implement interactions with user-provided vector (bulk) The Notes: The The The object of the class derived from The The actual backend factory object is supposed to be provided by the
+backend implementation and declared in its header file. In addition to this,
+the The following example is taken from With the above declarations, it is enough to pass the Note that the backend source code is placed in the Copyright © 2013 Mateusz Loskot Copyright © 2012 Vadim Zeitlin Copyright © 2004-2006 Maciej Sobczak, Stephen Hutton The SOCI DB2 backend . 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: To establish a connection to the DB2 database, create a session object
+using the TODO TODO Supported, but with caution as it hasn't been extensively tested. Currently, not supported. Currently, not supported. Nesting statements are not processed by SOCI in any special way and
+they work as implemented by the DB2 database. Stored procedures are supported, with TODO None. None Copyright © 2013 Mateusz Loskot The SOCI Firebird backend is currently supported for use with Firebird 1.5. The Firebird backend requires Firebird's To establish a connection to a Firebird database, create a Session object
+using the firebird backend factory together with a connection string: or simply: The set of parameters used in the connection string for Firebird is: The following parameters have to be provided as part of the connection string :
+service, user, password. Role and charset parameters are optional. Once you have created a (See the SOCI basics and exchanging data documentation for general information on using the The Firebird backend supports the use of the SOCI When calling [1] There is also 64bit integer type for larger values which is
+currently not supported. (See the dynamic resultset binding documentation for general information on using the In addition to binding by position, the Firebird backend supports binding by name, via an overload of the It should be noted that parameter binding by name is supported only by means of emulation, since the underlying API used by the backend doesn't provide this feature. The Firebird backend has full support for SOCI's bulk operations interface. This feature is also supported by emulation. Transactions are also fully
+supported by the Firebird backend. In fact, there is always a transaction which is automatically commited in The Firebird backend supports working with data stored in columns of type Blob, via SOCI's It should by noted, that entire Blob data is fetched from database to allow random read and write access.
+This is because Firebird itself allows only writing to a new Blob or reading from existing one -
+modifications of existing Blob means creating a new one. Firebird backend hides those details from user. This feature is not supported by Firebird backend. This feature is not supported by Firebird backend. Firebird stored procedures can be executed by using SOCI's Procedure class. SOCI provides access to underlying datbabase APIs via several getBackEnd() functions, as described in the beyond SOCI documentation. The Firebird backend provides the following concrete classes for navite API access: The Firebird backend can throw instances of class The Firebird backend recognize the following configuration macros : Copyright © 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski (Follow the links to learn more about each backend.) Copyright © 2013 Mateusz Loskot Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton The SOCI MySQL backend should in principle work with every version of MySQL 5.x.
+Some of the features (transactions, stored functions) are not available when
+MySQL server doesn't support them. The SOCI MySQL backend requires MySQL's Note that the SOCI library itself depends also on To establish a connection to a MySQL server, create a The set of parameters used in the connection string for MySQL is: Once you have created a (See the SOCI basics and exchanging data documentation for general information on using the The MySQL backend supports the use of the SOCI When calling (See the dynamic resultset binding documentation for general information on using the In addition to binding by
+position, the MySQL backend supports
+binding by name, via an overload
+of the It should be noted that parameter binding of any kind is supported
+only by means of emulation, since the underlying API used by the backend
+doesn't provide this feature. The MySQL backend has full support for SOCI's bulk operations interface. This feature is also supported
+by emulation. Transactions are also
+supported by the MySQL backend. Please note, however, that transactions
+can only be used when the MySQL server supports them (it depends on
+options used during the compilation of the server; typically, but not
+always, servers >=4.0 support transactions and earlier versions do not)
+and only with appropriate table types.
+ SOCI Note that this does not mean you cannot use MySQL's The Nested statements are not supported by the MySQL backend. MySQL version 5.0 and later supports two kinds of
+stored routines: stored procedures and stored functions
+(for details, please consult the
+MySQL
+documentation). Stored functions can be executed by using
+SOCI's procedure class.
+There is currently no support for stored procedures. SOCI provides access to underlying datbabase APIs via several The MySQL backend provides the following concrete classes for native API access: None. None. Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton, Pawel Aleksander Fedorynski The SOCI ODBC backend is supported for use with ODBC 3. The SOCI ODBC backend requires the ODBC client library. To establish a connection to the ODBC database, create a Session object
+using the or simply: The set of parameters used in the connection string for ODBC is the same as accepted by the Once you have created a (See the SOCI basics and exchanging data documentation for general information on using the The ODBC backend supports the use of the SOCI When calling Not all ODBC drivers support all datatypes (See the dynamic resultset binding documentation for general information on using the In addition to binding by position, the ODBC backend supports binding by name, via an overload of the Apart from the portable "colon-name" syntax above, which is achieved by rewriting the query string, the backend also supports the ODBC ? syntax: The ODBC backend has support for SOCI's bulk operations interface. Not all ODBC drivers support bulk operations, the following is a list of some tested backends: Transactions are also fully supported by the ODBC backend, provided that they are supported by the underlying database. Not currently supported Not currently supported Not currently supported Not currently supported SOCI provides access to underlying datbabase APIs via several getBackEnd() functions, as described in the beyond SOCI documentation. The ODBC backend provides the following concrete classes for navite API access: The ODBC backend can throw instances of class The This backend supports Copyright © 2013 Mateusz Loskot Copyright © 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney The SOCI Oracle backend is currently supported for use with Oracle 10 or later. The SOCI Oracle backend requires Oracle's Note that the SOCI library itself depends also on To establish a connection to an Oracle database, create a The set of parameters used in the connection string for Oracle is: The first 3 of these parameters have to be provided as part of the connection string. The Once you have created a (See the SOCI basics and exchanging data documentation for general information on using the The Oracle backend supports the use of the SOCI When calling (See the dynamic resultset binding documentation for general information on using the In addition to binding by position, the Oracle backend supports binding by name, via an overload of the SOCI's use of ':' to indicate a value to be bound within a SQL string is consistant with the underlying Oracle client library syntax. The Oracle backend has full support for SOCI's bulk operations interface. Transactions are also fully supported by the Oracle backend,
+although transactions with non-default isolation levels have to be managed by explicit SQL statements. The Oracle backend supports working with data stored in columns of type Blob, via SOCI's Oracle rowid's are accessible via SOCI's The Oracle backend supports selecting into objects of type Oracle stored procedures can be executed by using SOCI's SOCI provides access to underlying datbabase APIs via several The Oracle backend provides the following concrete classes for navite API access: The Oracle backend can throw instances of class Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton The SOCI PostgreSQL backend is supported for use with PostgreSQL >= 7.3, although versions older than 8.0 will suffer from limited feature support. See below for details. The SOCI PostgreSQL backend requires PostgreSQL's Note that the SOCI library itself depends also on To establish a connection to the PostgreSQL database, create a The set of parameters used in the connection string for PostgreSQL is the same as accepted by the Once you have created a (See the SOCI basics and exchanging data documentation for general information on using the The PostgreSQL backend supports the use of the SOCI When calling (See the dynamic resultset binding documentation for general information on using the In addition to binding by position, the PostgreSQL backend supports binding by name, via an overload of the Apart from the portable "colon-name" syntax above, which is achieved by rewriting the query string, the backend also supports the PostgreSQL native numbered syntax: The use of native syntax is not recommended, but can be nevertheless imposed by switching off the query rewriting. This can be achieved by defining the macro The PostgreSQL backend has full support for SOCI's bulk operations interface. Transactions are also fully supported by the PostgreSQL backend. The PostgreSQL backend supports working with data stored in columns of type Blob, via SOCI's The concept of row identifier (OID in PostgreSQL) is supported via SOCI's rowid class. Nested statements are not supported by PostgreSQL backend. PostgreSQL stored procedures can be executed by using SOCI's procedure class. SOCI provides access to underlying datbabase APIs via several The PostgreSQL backend provides the following concrete classes for navite API access: None. To support older PostgreSQL versions, the following configuration macros are recognized: Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton The SOCI SQLite3 backend is supported for use with SQLite3 >= 3.1 The SOCI SQLite3 backend requires SQLite3's To establish a connection to the SQLite3 database, create a Session object
+using the The only option for the connection string is the name of the file to use as a database. Once you have created a (See the SOCI basics and exchanging data documentation for general information on using the The SQLite3 backend supports the use of the SOCI When calling For the SQLite3 backend, this type mapping is complicated by the fact the SQLite3 does not enforce types *, and makes no attempt to validate the type names used in table creation or alteration statements. SQLite3 will return the type as a string, SOCI will recognize the following strings and match them the corresponding SOCI types: * There is one case where SQLite3 enforces type. If a column is declared as "integer primary key", then SQLite3 uses that as an alias to the internal ROWID column that exists for every table. Only integers are allowed in this column. (See the dynamic resultset binding documentation for general information on using the In addition to binding by position, the SQLite3 backend supports binding by name, via an overload of the The backend also supports the SQLite3 native numbered syntax, "one or more literals can be replace by a parameter "?" or ":AAA" or "@AAA" or "$VVV" where AAA is an alphanumeric identifier and VVV is a variable name according to the syntax rules of the TCL programming language." [1]: The SQLite3 backend has full support for SOCI's bulk operations interface. However, this support is emulated and is not native. Transactions are also fully supported by the SQLite3 backend. The SQLite3 backend supports working with data stored in columns of type Blob, via SOCI's blob class. Because of SQLite3 general typelessness the column does not have to be declared any particular type. In SQLite3 RowID is an integer. "Each entry in an SQLite table has a unique integer key called the "rowid". The rowid is always available as an undeclared column named ROWID, OID, or _ROWID_. If the table has a column of type INTEGER PRIMARY KEY then that column is another an alias for the rowid."[2] Nested statements are not supported by SQLite3 backend. Stored procedures are not supported by SQLite3 backend SOCI provides access to underlying datbabase APIs via several
+ The SQLite3 backend provides the following concrete classes for navite API access: None. None Copyright © 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney Sometimes the standard SQL is not enough and database-specific syntax needs
+to be used. When possible and practical, SOCI provides wrappers hiding the
+differences between the backends and this section describes these wrappers.
+And if this is still not enough, you can use the backend-specific methods
+directly as described below. 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 Portability note: It is common to have auto-incrementing database fields or fields whose
+value come from a sequence. In the latter case you need to retrieve the value
+of the field for a new row before inserting it into the database. In the
+former case, this is unnecessary but you may still want to know the value
+generated by the database, e.g. to use it as a foreign key in another table.
+So it would be useful to have a way to obtain the value of such a field.
+But, of course, to make life of database programmers more interesting,
+different products usually support either autoincrement fields or sequences
+but not both -- and they use different syntaxes for them, too. SOCI tries to
+help to deal with this unfortunate situation by providing two functions:
+ If you know which kind of database you use, you may use only one of them:
+when working with sequences, the first one allows to generate the next value
+in a sequence and when working with autoincrement fields, the second one
+retrieves the last value generated for such a field for the given table. 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: Portability note: As the original name of the library (Simple Oracle Call Interface)
+clearly stated, SOCI is intended to be a simple library, targeting the
+majority of needs in regular C++ application. We do not claim that
+everything can be done with SOCI and it was never the intent of the
+library. What is important, though, is that the simplicity of the
+library does not prevent the
+client applications from reaching into the low-level specifics of each
+database backend in order to achieve special configuration or
+performance goals. Most of the SOCI classes have the The above code creates the The above example retrieves the In order for any of the above to compile, you have to explicitly Please see the header file related to the given backend to learn what
+low-level handles and descriptors are available. Copyright © 2012 Vadim Zeitlin Copyright © 2004-2006 Maciej Sobczak, Stephen Hutton The SOCI user code can be easily integrated with the Boost library thanks to the very flexible type conversion facility. There are three important Boost types that are supported out of the box. The Example: The Example: Tuples are supported for both Tuples can be also composed with The The Optional integration: The integration with Boost types is optional and not enabled by default, which means that SOCI can be compiled and used without any dependency on Boost. In order to enable the support for any of the above types, the user needs to either include one of these headers: or to define the Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton The The most basic one expects two parameters:
+the requested backend factory object and the generic connection string,
+which meaning is backend-dependent. Example: Another example might be: Above, the This Another constructor allows to name backends at run-time and supports
+the dynamically loadable backends, which have to be compiled as shared libraries. The usage is similar to the above, but instead of providing the factory object, the backend name is expected: For convenience, the URL-like form that combines both the backend name with connection parameters is supported as well: The last two constructors described above try to locate the shared library with the name The most general form of the constructor takes a single object of Environment configuration: The The run-time selection of backends is also supported with libraries
+linked statically. Each backend provides a separate function of the
+form 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: The rules for backend naming are the same as with the constructors described above. The session can be also explicitly See also the page devoted to multithreading for a detailed description of connection pools. It is possible to have many active Portability note: The following backend factories are currently (as of 3.1.0 release) available: The following backends are also available, with various levels of completeness: Copyright © 2013 Mateusz Loskot Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton All DB-related errors manifest themselves as exceptions of type Portability note: The Oracle backend can also throw the instances of the Portability note: The MySQL backend can throw instances of the Portability note: The PostgreSQL backend can also throw the instances of the Copyright © 2004-2013 Maciej Sobczak, Stephen Hutton, Mateusz Loskot Note:
+The Oracle documentation uses two terms: defining (for
+instructing the library where the output data should go) and binding
+(for the input data and input/output PL/SQL
+parameters). For the sake of simplicity, SOCI uses the term binding
+for both of these. The In the above examples, some data is retrieved from the database and
+transmitted into the given local variable. There should be as many The In the above statement, the first "val" is a column name (assuming
+that
+there is appropriate table To better understand the meaning of each "val" above, consider also: Both examples above will insert the value of some local variable into
+the table There should be as many Portability note: Older versions of the PostgreSQL client API do not allow to use input
+parameters at all. In order to compile SOCI with those old client
+libraries, define the If there is more output or input "holes" in the single statement, it
+is possible to use many In the code above, the order of "holes" in the SQL statement and the
+order of The SQL placeholders that have their names (with colon) can be bound
+by name to clearly associate the local variable with the given placeholder. This explicit naming allows to use different order of elements: or bind the same local data to many "holes" at the same time: Object lifetime and immutability: SOCI assumes that local variables provided as In the above example, the data passed to the database comes from the temporary
+variable that is a result of call to It is still possible to provide The above example can be ultimately written in the following way: Portability notes: The PostgreSQL backend allows to use the "native" PostgreSQL way of
+naming parameters in the query, which is by numbers like In order to support null values and other conditions which are not
+real errors, the concept of indicator is provided. For example, when the following SQL query is executed: there are three possible outcomes: 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 use of indicator variable is optional, but if it is not used and
+the result would be 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: In the above example, the row is inserted with Indicator variables can also be used in conjunction with vector
+based insert, update, and select statements: The above example retrieves first 100 rows of data (or less). The
+initial size of The following example inserts null for each value of name: See also Integration with Boost to learn
+how the Boost.Optional library can be used to handle null data conditions
+in a more natural way. The static binding for types is most useful when the types used in
+the database are known at compile time - this was already presented
+above with the help of The following types are currently supported for use with See the test code that accompanies the library to see how each of
+these types is used. Bulk inserts, updates, and selects are supported through the
+following Use of the vector based types mirrors that of the standard types, with
+the size of the vector used to specify the number of records to process
+at a time. See below for examples. Note that bulk operations are supported only for For certain applications it is desirable to be able to select data from
+arbitrarily structured tables (e.g. via " Data is selected into a For example, the code below creates an XML document from a selected row
+of data from an arbitrary table: The type The mapping of underlying database column types to SOCI datatypes is database specific.
+See the backend documentation for details. The It is also possible to extract data from the Note, however, that this interface is not compatible with the standard
+ SOCI can be easily extended with support for user-defined datatypes. The extension mechanism relies on appropriate specialization of the There are three required class members for a valid 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 The above specialization for Note that there is a number of types from the Boost library integrated with SOCI out of the box, see Integration with Boost for complete description. Use these as examples of conversions for more complext data types. Note also that user-defined datatypes are not supported with bulk data transfer. Another possibility to extend SOCI with custom data types is to use
+the Note that when specializing these template classes the only convention
+is that when the indicator
+variable is used (see below), it should appear in the second position.
+Please refer to the library source code to see how this is done for the
+standard types. SOCI provides a class called For example, the following code maps a Note that the mapping is non-invasive - the With the above Note: The The SOCI library provides also an interface for basic operations on
+large objects (BLOBs - Binary Large OBjects). The following functions are provided in the The Portability notes: Copyright © 2010-2013 Mateusz Loskot Copyright © 2012 Vadim Zeitlin Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton The following (complete!) example is purposedly provided without any explanation. Copyright © 2010-2013 Mateusz Loskot Copyright © 2012 Vadim Zeitlin Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton Below is an overall list of SOCI core: and backend-specific dependencies: Download package with latest release of the SOCI source code: soci-X.Y.Z, where X.Y.Z is the version number. Unpack the archive. You can always clone SOCI from the Git repository: SOCI is configured to build using CMake system in version 2.8+. The build configuration allows to control various aspects of compilation and installation by setting common CMake variables that change behaviour, describe system or control build (see CMake help) as well as SOCI-specific variables described below. All these variables are available regardless of platform or compilation toolset used. Running CMake from the command line allows to set variables in the CMake cache with the following syntax: The following tables provide summary of variables accepted by CMake scripts configuring SOCI build. The lists consist of common variables for SOCI core and all backends as well as variables specific to SOCI backends and their direct dependencies. By default, CMake will try to determine availability of all depdendencies automatically. If you are lucky, you will not need to specify any of the CMake variables explained above. However, if CMake reports some of the core or backend-specific dependencies as missing, you will need specify relevant variables to tell CMake where to look for the required components. CMake configures SOCI build performing sequence of steps. Each
+subsequent step is dependant on result of previous steps corresponding
+with particular feature. First, CMake checks system platform and
+compilation toolset. Next, CMake tries to find all external
+dependencies. Then, depending on the results of the dependency check,
+CMake determines SOCI backends which are possible to build. The
+SOCI-specific variables described above provide users with basic
+control of this behaviour. Short version using GNU Make makefiles: Short version using Visual Studio 2010 and MSBuild: NOTE: These Makefiles have not been maintained for long time.
+The officially maintained build configuration is CMake. If you still want to
+use these Makefiles, you've been warned that you may need to patch them. The classic set of Makefiles for Unix/Linux systems is provided for those users who need complete control over the whole process
+and who can benefit from the basic scaffolding that they can extend on their own.
+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. The For example, the simplest way to compile the static version of the
+library and the test program for PostgreSQL is: Note:
+For each backend and its test program, the The Makefiles for test programs can be a good starting point to find out correct compiler and linker options. The process of running regression tests highly depends on user's environment and build configuration, so it may be quite involving process. The CMake configuration provides variables to allow users willing to run the tests to configure build and specify database connection parameters (see the tables above for variable names). In order to run regression tests, configure and build desired SOCI backends and prepare working database instances for them. While configuring build with CMake, specify Dedicated In the example above, regression tests for the sample Empty backend and SQLite 3 backend are configured for execution by CMake build produces set of shared and static libraries for SOCI core and backends separately. On Unix, for example, In order to use SOCI in your program, you need to specify your project build configuration with paths to SOCI headers and libraries,
+and specify linker to link against the libraries you want to use in your program.Backends reference
+
+core/soci-backend.h
+header file. This file is dissected below.soci or soci::details
+namespace.
+// data types, as seen by the user
+enum data_type
+{
+ dt_string, dt_date, dt_double, dt_integer, dt_long_long, dt_unsigned_long_long
+};
+
+// the enum type for indicator variables
+enum indicator { i_ok, i_null, i_truncated };
+
+// data types, as used to describe exchange format
+enum exchange_type
+{
+ x_char,
+ x_stdstring,
+ x_short,
+ x_integer,
+ x_long_long,
+ x_unsigned_long_long,
+ x_double,
+ x_stdtm,
+ x_statement,
+ x_rowid,
+ x_blob
+};
+
+struct cstring_descriptor
+{
+ cstring_descriptor(char * str, std::size_t bufSize)
+ : str_(str), bufSize_(bufSize) {}
+
+ char * str_;
+ std::size_t bufSize_;
+};
+
+// actually in error.h:
+class soci_error : public std::runtime_error
+{
+public:
+ soci_error(std::string const & msg);
+};
+
+
+data_type enumeration type defines all types that
+form the core type support for SOCI. The enum itself can be used by
+clients when dealing with dynamic rowset description.indicator enumeration type defines all recognized states of data.
+The i_truncated
+state is provided for the case where the string is retrieved from the
+database into the char buffer that is not long enough to hold the whole
+value.exchange_type enumeration type defines all possible
+types that can be used with the into and use
+elements.cstring_descriptor is a helper class that allows to
+store the address of char buffer together with its size.
+The objects of this class are passed to the backend when the x_cstring
+type is involved.soci_error class is an exception type used for
+database-related (and
+also usage-related) errors. The backends should throw exceptions of
+this or derived type only.
+class standard_into_type_backend
+{
+public:
+ standard_into_type_backend() {}
+ virtual ~standard_into_type_backend() {}
+
+ virtual void define_by_pos(int& position, void* data, exchange_type type) = 0;
+
+ virtual void pre_fetch() = 0;
+ virtual void post_fetch(bool gotData, bool calledFromFetch, indicator* ind) = 0;
+
+ virtual void clean_up() = 0;
+};
+
+
+standard_into_type_back_end class implements the dynamic
+interactions with the simple (non-bulk) into elements.
+The objects of this class (or, rather, of the derived class implemented
+by the actual backend) are created by the statement
+object when the into element is bound - in terms of
+lifetime management, statement is the master of this
+class.
+
+
+define_by_pos - Called when the into
+element is bound, once and before the statement is executed. The data
+pointer points to the variable used for into element (or
+to the cstring_descriptor object, which is artificially
+created when the plain char buffer is used for data
+exchange). The position parameter is a "column number",
+assigned by
+the library. The backend should increase this parameter, according to
+the number of fields actually taken (usually 1).pre_fetch - Called before each row is fetched.post_fetch - Called after each row is fetched. The gotData
+parameter is true if the fetch operation really retrieved
+some data and false otherwise; calledFromFetch
+is true when the call is from the fetch operation and false
+if it is from the execute operation (this is also the case for simple,
+one-time queries). In particular, (calledFromFetch &&
+!gotData) indicates that there is an end-of-rowset condition. ind
+points to the indicator provided by the user, or is NULL,
+if there is no indicator.clean_up - Called once when the statement is
+destroyed.pre_fetch and post_fetch
+functions is to manage any internal buffer and/or data conversion for
+each value retrieved from the database. If the given server supports
+binary data transmission and the data format for the given type agrees
+with what is used on the client machine, then these two functions need
+not do anything; otherwise buffer management and data conversions
+should go there.
+class vector_into_type_backend
+{
+public:
+ vector_into_type_backend() {}
+ virtual ~vector_into_type_backend() {}
+
+ virtual void define_by_pos(int& position, void* data, exchange_type type) = 0;
+
+ virtual void pre_fetch() = 0;
+ virtual void post_fetch(bool gotData, indicator* ind) = 0;
+
+ virtual void resize(std::size_t sz) = 0;
+ virtual std::size_t size() = 0;
+
+ virtual void clean_up() = 0;
+};
+
+
+vector_into_type_back_end has similar structure and
+purpose as the previous one, but is used for vectors (bulk data
+retrieval).data pointer points to the variable of type std::vector<T>
+(and not to its internal buffer), resize
+is supposed to really resize the user-provided vector and size
+is supposed to return the current size of this vector.
+The important difference with regard to the previous class is that ind
+points (if not NULL) to the beginning of the array of indicators. The backend
+should fill this array according to the actual state of the retrieved
+data.
+class standard_use_type_backend
+{
+public:
+ virtual ~standard_use_type_backend() {}
+
+ virtual void bind_by_pos(int& position,
+ void* data, exchange_type type, bool readOnly) = 0;
+ virtual void bind_by_name(std::string const& name,
+ void* data, exchange_type type, bool readOnly) = 0;
+
+ virtual void pre_use(indicator const* ind) = 0;
+ virtual void post_use(bool gotData, indicator* ind) = 0;
+
+ virtual void clean_up() = 0;
+};
+
+
+standard_use_type_backend implements the interactions
+with the simple (non-bulk) use elements, created and
+destroyed by the statement object.
+
+
+bind_by_pos - Called for each use
+element, once and before the statement is executed - for those use
+elements that do not provide explicit names for parameter binding. The
+meaning of parameters is same as in previous classes.bind_by_name - Called for those use
+elements that provide the explicit name.pre_use - Called before the data is transmitted to
+the server (this means before the statement is executed, which can
+happen many times for the prepared statement). ind points
+to the indicator provided by the user (or is NULL).post_use - Called after statement execution. gotData
+and ind have the same meaning as in standard_into_type_back_end::post_fetch,
+and this can be used by those backends whose respective servers support
+two-way data exchange (like in/out parameters in stored procedures).pre_use and post_use
+methods is to manage any internal buffers and/or data conversion. They
+can be called many times with the same statement.
+class vector_use_type_backend
+{
+public:
+ virtual ~vector_use_type_backend() {}
+
+ virtual void bind_by_pos(int& position,
+ void* data, exchange_type type) = 0;
+ virtual void bind_by_name(std::string const& name,
+ void* data, exchange_type type) = 0;
+
+ virtual void pre_use(indicator const* ind) = 0;
+
+ virtual std::size_t size() = 0;
+
+ virtual void clean_up() = 0;
+};
+
+
+use
+elements and are managed by the statement object.
+The data pointer points to the whole vector object
+provided by the user (and not to
+its internal buffer); ind points to the beginning of the
+array of indicators (or is NULL). The meaning of this
+interface is analogous to those presented above.
+class statement_backend
+{
+public:
+ statement_backend() {}
+ virtual ~statement_backend() {}
+
+ virtual void alloc() = 0;
+ virtual void clean_up() = 0;
+
+ virtual void prepare(std::string const& query, statement_type eType) = 0;
+
+ enum exec_fetch_result
+ {
+ ef_success,
+ ef_no_data
+ };
+
+ virtual exec_fetch_result execute(int number) = 0;
+ virtual exec_fetch_result fetch(int number) = 0;
+
+ virtual long long get_affected_rows() = 0;
+ virtual int get_number_of_rows() = 0;
+
+ virtual std::string rewrite_for_procedure_call(std::string const& query) = 0;
+
+ virtual int prepare_for_describe() = 0;
+ virtual void describe_column(int colNum, data_type& dtype,
+ std::string& column_name) = 0;
+
+ virtual standard_into_type_backend* make_into_type_backend() = 0;
+ virtual standard_use_type_backend* make_use_type_backend() = 0;
+ virtual vector_into_type_backend* make_vector_into_type_backend() = 0;
+ virtual vector_use_type_backend* make_vector_use_type_backend() = 0;
+};
+
+
+statement_backend type implements the internals of the
+statement objects. The objects of this class are created by the session
+object.
+
+
+alloc - Called once to allocate everything that is
+needed for the statement to work correctly.clean_up - Supposed to clean up the resources, called
+once.prepare - Called once with the text of the SQL
+query. For servers that support explicit query preparation, this is the
+place to do it.execute - Called to execute the query; if number is
+zero, the intent is not to exchange data with the user-provided objects
+(into and use elements); positive values
+mean the number of rows to exchange (more than 1 is used only for bulk
+operations).fetch - Called to fetch next bunch of rows; number
+is positive and determines the requested number of rows (more than 1 is
+used only for bulk operations).get_affected_rows - Called to determine the actual
+number of rows affected by data modifying statement.get_number_of_rows - Called to determine the actual
+number of rows retrieved by the previous call to execute
+or fetch.rewrite_for_procedure_call - Used when the procedure
+is used instead of statement, to call the stored
+procedure. This function should rewrite the SQL query (if necessary) to
+the form that will allow to execute the given procedure.
+ prepare_for_describe - Called once when the into
+element is used with the row type, which means that
+dynamic rowset description should be performed. It is supposed to do
+whatever is needed to later describe the column properties and should
+return the number of columns.describe_column - Called once for each column (column
+numbers - colNum - start from 1), should fill its
+parameters according to the column properties.make_into_type_backend, make_use_type_backend,
+ make_vector_into_type_backend, make_vector_use_type_backend
+- Called once for each into or use element,
+to create the objects of appropriate classes (described above).
+
+
+alloc, prepare and execute
+functions are always called, in this order.into and use elements are bound
+(their define_by_pos or bind_by_pos/bind_by_name
+functions are called) between
+statement preparation and execution.
+
+class rowid_backend
+{
+public:
+ virtual ~rowid_backend() {}
+};
+
+
+rowid_backend class is a hook for the backends to
+provide their own state for the row identifier. It has no functions,
+since the only portable interaction with the row identifier object is
+to use it with into and use elements.
+class blob_backend
+{
+public:
+ virtual ~blob_backend() {}
+
+ virtual std::size_t get_len() = 0;
+ virtual std::size_t read(std::size_t offset, char * buf,
+ std::size_t toRead) = 0;
+ virtual std::size_t write(std::size_t offset, char const * buf,
+ std::size_t toWrite) = 0;
+ virtual std::size_t append(char const * buf, std::size_t toWrite) = 0;
+ virtual void trim(std::size_t newLen) = 0;
+};
+
+
+blob_backend interface provides the entry points for
+the blob methods.
+class session_backend
+{
+public:
+ virtual ~session_backend() {}
+
+ virtual void begin() = 0;
+ virtual void commit() = 0;
+ virtual void rollback() = 0;
+
+ virtual bool get_next_sequence_value(session&, std::string const&, long&);
+ virtual bool get_last_insert_id(session&, std::string const&, long&);
+
+ virtual std::string get_backend_name() const = 0;
+
+ virtual statement_backend * make_statement_backend() = 0;
+ virtual rowid_backend * make_rowid_backend() = 0;
+ virtual blob_backend * make_blob_backend() = 0;
+};
+
+
+session_backend implements the
+internals of the session object.
+
+
+begin, commit, rollback
+- Forward-called when the same functions of session are
+called by user.get_next_sequence_value, get_last_insert_id
+- Called to retrieve sequences or auto-generated values and every backend should
+define at least one of them to allow the code using auto-generated values to work.
+ make_statement_backend, make_rowid_backend,
+ make_blob_backend - Called to create respective
+implementations for the statement, rowid
+and blob classes.
+
+struct backend_factory
+{
+ virtual ~backend_factory() {}
+
+ virtual details::session_backend * make_session(
+ std::string const& connectString) const = 0;
+};
+
+
+backend_factory is a base class for backend-provided
+factory class that is able to create valid sessions. The connectString
+parameter passed to make_session is provided here by the session
+constructor and contains only the backend-related parameters, without the backend name
+(if the dynamic backend loading is used).factory_ABC function with the "C" calling convention
+and returning the pointer to concrete factory object should be provided,
+where ABC is the backend name.soci-postgresql.h,
+which declares entities of the PostgreSQL backend:
+struct postgresql_backend_factory : backend_factory
+{
+ virtual postgresql_session_backend* make_session(
+ std::string const& connectString) const;
+};
+
+extern postgresql_backend_factory const postgresql;
+
+extern "C"
+{
+
+// for dynamic backend loading
+backend_factory const * factory_postgresql();
+
+} // extern "C"
+
+
+postgresql
+factory name to the constructor of the session object,
+which will use this factory to create concrete implementations for any
+other objects that
+are needed, with the help of appropriate make_XYZ
+functions. Alternatively, the factory_postgresql function will
+be called automatically by the backend loader if the backend name is provided
+at run-time instead.backends/name directory (for example,
+backends/oracle) and the test driver is in backends/name/test. There is also backends/empty
+directory provided as a skeleton for development of new backends and
+their tests. It is
+recommended that all backends respect naming conventions by just
+appending their name to the base-class names. The backend name used for
+the global factory object should clearly identify the given
+database engine, like oracle, postgresql, mysql,
+and so on.
+
+
+
+
+
+ Previous (Client reference)
+
+
+ Next (Rationale FAQ)
+
+ DB2 Backend Reference
+
+
+
+Prerequisites
+Supported Versions
+
+Tested Platforms
+
+
+
+
+
+
+DB2 version Operating System Compiler
+- Linux PPC64 GCC
+9.1 Linux GCC
+9.5 Linux GCC
+9.7 Linux GCC
+10.1 Linux GCC
+
+10.1 Windows 8 Visual Studio 2012 Required Client Libraries
+
+Connecting to the Database
+
+. ~/db2inst1/sqllib/db2profile
+
+DB2 backend factory together with the database file name:
+soci::session sql(soci::db2, "your DB2 connection string here");
+
+
+SOCI Feature Support
+Dynamic Binding
+
+Binding by Name
+
+Bulk Operations
+
+Transactions
+
+BLOB Data Type
+
+Nested Statements
+
+Stored Procedures
+
+CALL statement.Acessing the native database API
+
+Backend-specific extensions
+
+Configuration options
+
+Firebird Backend Reference
+
+
+
+Prerequisites
+Supported Versions
+
+
+Other versions of Firebird may work as well, but they have not been tested by the SOCI team.Tested Platforms
+
+
+
+
+
+
+Firebird version Operating System Compiler
+1.5.2.4731 SunOS 5.10 g++ 3.4.3
+1.5.2.4731 Windows XP Visual C++ 8.0
+
+1.5.3.4870 Windows XP Visual C++ 8.0 Professional Required Client Libraries
+
+libfbclient client library.Connecting to the Database
+
+
+BackEndFactory const &backEnd = firebird;
+Session sql(backEnd,
+ "service=/usr/local/firbird/db/test.fdb user=SYSDBA password=masterkey");
+
+
+
+Session sql(firebird,
+ "service=/usr/local/firbird/db/test.fdb user=SYSDBA password=masterkey");
+
+
+
+
+Session object as shown above, you can use it to access the database, for example:
+int count;
+sql << "select count(*) from user_tables", into(count);
+
+
+Session class.)SOCI Feature Support
+Dynamic Binding
+
+Row class, which facilitates retrieval of data whose type is not known at compile time.Row::get<T>(), the type you should pass as T depends upon the underlying database type. For the Firebird backend, this type mapping is:
+
+
+
+
+ Firebird Data Type
+ SOCI Data Type
+
+ Row::get<T> specializations
+
+ numeric, decimal
+
(where scale > 0)
+ eDouble
+ double
+
+ numeric, decimal [1]
+
(where scale = 0)
+ eInteger, eDouble
+ int, double
+
+ double precision, float
+
+ eDouble
+ double
+
+ smallint, integer
+
+ eInteger
+ int
+
+ char, varchar
+
+ eString
+ std::string
+
+
+date, time, timestamp
+
+ eDate
+ std::tmRow class.)Binding by Name
+
+use() function:
+int id = 7;
+sql << "select name from person where id = :id", use(id, "id")
+
+
+Bulk Operations
+
+Transactions
+
+Session's destructor.
+
See the Configuration options section for more details.BLOB Data Type
+
+BLOB class.RowID Data Type
+
+Nested Statements
+
+Stored Procedures
+
+Acessing the native database API
+
+
+
+
+
+
+
+
+ Accessor Function
+ Concrete Class
+
+
+
+ SessionBackEnd* Session::getBackEnd()
+ FirebirdSessionBackEnd
+
+
+ StatementBackEnd* Statement::getBackEnd()
+ FirebirdStatementBackEnd
+
+
+ BLOBBackEnd* BLOB::getBackEnd()
+ FirebirdBLOBBackEnd
+
+
+
+ RowIDBackEnd* RowID::getBackEnd()
+ FirebirdRowIDBackEndBackend-specific extensions
+FirebirdSOCIError
+
+FirebirdSOCIError,
+which is publicly derived from SOCIError and has an
+additional public status_ member containing the Firebird status vector.Configuration options
+
+
+
+
+SOCI_FIREBIRD_NORESTARTTRANSACTION -
+ Transactions will not be restarted automatically after commit() or rollback().
+ The default is to restart transactions.Existing backends and supported platforms
+
+Supported Features
+
+
+
+
+
+
+
+
+ Oracle
+ PostgreSQL
+ MySQL
+ SQLite3
+ Firebird
+ ODBC
+ DB2
+
+
+ Binding by Name
+ YES
+ YES (>=8.0)
+ YES
+ YES
+ YES
+ YES
+ YES
+
+
+ Dynamic Binding
+ YES
+ YES
+ YES
+ YES
+ YES
+ YES
+
+
+
+ Bulk Operations
+ YES
+ YES
+ YES
+ YES
+ YES
+ YES
+ YES
+
+
+ Transactions
+ YES
+ YES
+ YES
+ (with servers that support them, usually >= 4.0)
+ YES
+ YES
+ YES
+ YES
+
+
+ BLOB Data Type
+ YES
+ YES
+ MySQL's BLOB type is mapped to
+ std::stringYES
+ YES
+ NO
+ NO
+
+
+ RowID Data Type
+ YES
+ YES
+ NO
+ NO
+ NO
+ NO
+ NO
+
+
+ Nested Statements
+ YES
+ NO
+ NO
+ NO
+ NO
+ NO
+ YES
+
+
+
+Stored Procedures
+ YES
+ YES
+ NO (but stored functions, YES)
+ NO
+ YES
+ NO
+ YES
+
+
+
+
+
+
+ Previous (Rationale FAQ)
+
+ MySQL Backend Reference
+
+
+
+Prerequisites
+Supported Versions
+
+Tested Platforms
+
+
+
+
+
+
+MySQL version Operating System Compiler
+5.5.28 OS X 10.8.2 Apple LLVM version 4.2
+(clang-425.0.24)
+
+5.0.96 Ubuntu 8.04.4 LTS (Hardy Heron)
+g++ (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)
+Required Client Libraries
+
+libmysqlclient
+client library.libdl, so the minimum set of libraries needed to compile a basic client program is:
+-lsoci_core -lsoci_mysql -ldl -lmysqlclient
+
+
+
+Connecting to the Database
+
+session object
+using the mysql backend factory together with a connection
+string:
+session sql(mysql, "db=test user=root password='Ala ma kota'");
+
+// or:
+session sql("mysql", "db=test user=root password='Ala ma kota'");
+
+// or:
+session sql("mysql://db=test user=root password='Ala ma kota'");
+
+
+
+
+
+dbname or db or service
+ (required)userpassword or passhostportunix_socketsslcasslcertlocal_infile - should be 0 or 1,
+ 1 means MYSQL_OPT_LOCAL_INFILE will be set.charsetsession object as shown above, you
+can use it to access the database, for example:
+int count;
+sql << "select count(*) from invoices", into(count);
+
+
+session class.)SOCI Feature Support
+Dynamic Binding
+
+row class,
+which facilitates retrieval of data which type is not known at compile
+time.row::get<T>(), the type you should pass
+as T depends upon the underlying database type.
+For the MySQL backend, this type mapping is:
+
+
+
+
+
+ MySQL Data Type
+ SOCI Data Type
+
+ row::get<T> specializations
+
+ FLOAT, DOUBLE, DECIMAL and synonyms
+
+ dt_double
+ double
+
+ TINYINT, TINYINT UNSIGNED, SMALLINT, SMALLINT UNSIGNED, INT
+
+ dt_integer
+ int
+
+ INT UNSIGNED
+
+ dt_long_long
+ long long or unsigned
+
+ BIGINT
+
+ dt_long_long
+ long long
+
+ BIGINT UNSIGNED
+
+ dt_unsigned_long_long
+ unsigned long long
+
+ CHAR, VARCHAR, BINARY, VARBINARY, TINYBLOB, MEDIUMBLOB, BLOB,
+ LONGBLOB, TINYTEXT, MEDIUMTEXT, TEXT, LONGTEXT, ENUM
+
+ dt_string
+ std::string
+
+
+TIMESTAMP (works only with MySQL >= 5.0), DATE,
+ TIME, DATETIME
+
+ dt_date
+ std::tmRow class.)Binding by Name
+
+use() function:
+int id = 7;
+sql << "select name from person where id = :id", use(id, "id")
+
+
+Bulk Operations
+
+Transactions
+
+BLOB Data Type
+
+blob interface is not supported by the MySQL backend.BLOB
+types. They can be selected using the usual SQL syntax and read into
+std::string on the C++ side, so no special interface is
+required.RowID Data Type
+
+rowid functionality is not supported by the MySQL backend.Nested Statements
+
+Stored Procedures
+
+Accessing the native database API
+
+get_backend() functions, as described in the Beyond SOCI documentation.
+
+
+
+
+
+
+ Accessor Function
+ Concrete Class
+
+
+
+ session_backend * session::get_backend()
+ mysql_session_backend
+
+
+
+ statement_backend * statement::get_backend()
+ mysql_statement_backendBackend-specific extensions
+
+Configuration options
+
+ODBC Backend Reference
+
+
+
+Prerequisites
+Supported Versions
+
+Tested Platforms
+
+
+
+
+
+
+ODBC version Operating System Compiler
+3 Linux (Ubuntu 12.04) g++ 4.6.3
+3 Linux (Ubuntu 12.04) clang 3.2
+3.8 Windows 8 Visual Studio 2012
+3 Windows 7 Visual Studio 2010
+3 Windows XP Visual Studio 2005 (express)
+3 Windows XP Visual C++ 8.0 Professional
+
+3 Windows XP g++ 3.3.4 (Cygwin) Required Client Libraries
+
+Connecting to the Database
+
+ODBC backend factory together with a connection string:
+backend_factory const& backEnd = odbc;
+session sql(backEnd, "filedsn=c:\\my.dsn");
+
+
+
+session sql(odbc, "filedsn=c:\\my.dsn");
+
+
+SQLDriverConnect function from the ODBC library.session object as shown above, you can use it to access the database, for example:
+int count;
+sql << "select count(*) from invoices", into(count);
+
+
+session class.)SOCI Feature Support
+Dynamic Binding
+
+row class, which facilitates retrieval of data whose type is not known at compile time.row::get<T>(), the type you should pass as T depends upon the underlying database type.
For the ODBC backend, this type mapping is:
+
+
+
+
+
+ ODBC Data Type
+ SOCI Data Type
+
+ row::get<T> specializations
+
+ SQL_DOUBLE
+ , SQL_DECIMAL
+ , SQL_REAL
+ , SQL_FLOAT
+ , SQL_NUMERIC
+
+
+ dt_double
+ double
+
+ SQL_TINYINT
+ , SQL_SMALLINT
+ , SQL_INTEGER
+ , SQL_BIGINT
+
+ dt_integer
+ int
+
+ SQL_CHAR, SQL_VARCHAR
+
+ dt_string
+ std::string
+
+
+SQL_TYPE_DATE
+ , SQL_TYPE_TIME
+ , SQL_TYPE_TIMESTAMP
+
+ dt_date
+ std::tmrow class.)Binding by Name
+
+use() function:
+int id = 7;
+sql << "select name from person where id = :id", use(id, "id")
+
+
+
+int i = 7;
+int j = 8;
+sql << "insert into t(x, y) values(?, ?)", use(i), use(j);
+
+
+Bulk Operations
+
+
+
+
+
+
+
+ ODBC Driver
+ Bulk Read
+ Bulk Insert
+
+
+ MS SQL Server 2005
+ YES
+ YES
+
+
+ MS Access 2003
+ YES
+ NO
+
+
+ PostgresQL 8.1
+ YES
+ YES
+
+
+
+MySQL 4.1
+ NO
+ NO
+ Transactions
+
+BLOB Data Type
+
+RowID Data Type
+
+Nested Statements
+
+Stored Procedures
+
+Acessing the native database API
+
+
+
+
+
+
+
+
+ Accessor Function
+ Concrete Class
+
+
+
+ session_backend* session::get_backend()
+ odbc_statement_backend
+
+
+ statement_backend* statement::get_backend()
+ odbc_statement_backend
+
+
+
+ rowid_backend* rowid::get_backend()
+ odbc_rowid_backendBackend-specific extensions
+odbc_soci_error
+
+odbc_soci_error,
+which is publicly derived from soci_error and has
+additional public members containing the ODBC error code, the Native database
+error code, and the message returned from ODBC:
+int main()
+{
+ try
+ {
+ // regular code
+ }
+ catch (soci::odbc_soci_error const& e)
+ {
+ cerr << "ODBC Error Code: " << e.odbc_error_code() << endl
+ << "Native Error Code: " << e.native_error_code() << endl
+ << "SOCI Message: " << e.what() << std::endl
+ << "ODBC Message: " << e.odbc_error_message() << endl;
+ }
+ catch (exception const &e)
+ {
+ cerr << "Some other error: " << e.what() << endl;
+ }
+}
+
+
+get_connection_string()
+odbc_session_backend class provides std::string get_connection_string() const method
+that returns fully expanded connection string as returned by the SQLDriverConnect function.Configuration options
+
+odbc_option_driver_complete option which can be passed to it via connection_parameters class. The value of this option is passed to SQLDriverConnect() function as "driver completion" parameter and so must be one of SQL_DRIVER_XXX values, in the string form. The default value of this option is SQL_DRIVER_PROMPT meaning that the driver will query the user for the user name and/or the password if they are not stored together with the connection. If this is undesirable for some reason, you can use SQL_DRIVER_NOPROMPT value for this option to suppress showing the message box:
+connection_parameters parameters("odbc", "DSN=mydb");
+parameters.set_option(odbc_option_driver_complete, "0" /* SQL_DRIVER_NOPROMPT */);
+session sql(parameters);
+
+
+Oracle Backend Reference
+
+
+
+Prerequisites
+Supported Versions
+
+
+Older versions of Oracle may work as well, but they have not been tested by the SOCI team.Tested Platforms
+
+
+
+
+
+
+Oracle version Operating System Compiler
+
+10.2.0 (XE) RedHat 5 g++ 4.3 Required Client Libraries
+
+libclntsh client library.
+Depending on the particular system, the libnnz10 library might be needed as well.libdl, so the minimum set of libraries needed to compile a basic client program is:
+-lsoci_core -lsoci_oracle -ldl -lclntsh -lnnz10
+
+
+Connecting to the Database
+
+session object
+using the oracle backend factory together with a connection string:
+session sql(oracle, "service=orcl user=scott password=tiger");
+
+// or:
+session sql("oracle", "service=orcl user=scott password=tiger");
+
+// or:
+session sql("oracle://service=orcl user=scott password=tiger");
+
+
+
+
+serviceuserpasswordmode (optional)mode parameter allows to specify the connection mode and can be any of:
+
+
+default (which is assumed if omitted)sysdbasysopersession object as shown above, you can use it to access the database, for example:
+int count;
+sql << "select count(*) from user_tables", into(count);
+
+
+session class.)SOCI Feature Support
+
+Dynamic Binding
+
+row class, which facilitates retrieval of data which type is not known at compile time.row::get<T>(), the type you should pass as T depends upon the underlying database type.
For the Oracle backend, this type mapping is:
+
+
+
+
+
+ Oracle Data Type
+ SOCI Data Type
+
+ row::get<T> specializations
+
+ number (where scale > 0)
+
+ dt_double
+ double
+
+ number
+
(where scale = 0 and precision ≤ std::numeric_limits<int>::digits10)
+ dt_integer
+ int
+
+ number
+
+ dt_long_long
+ long long
+
+ char, varchar, varchar2
+
+ dt_string
+ std::string
+
+
+date
+
+ dt_date
+ std::tmrow class.)Binding by Name
+
+use() function:
+int id = 7;
+sql << "select name from person where id = :id", use(id, "id")
+
+
+Bulk Operations
+
+Transactions
+
+blob Data Type
+
+blob class.rowid Data Type
+
+rowid class.Nested Statements
+
+statement, so that you may work with nested sql statements and PL/SQL cursors:
+statement stInner(sql);
+statement stOuter = (sql.prepare <<
+ "select cursor(select name from person order by id)"
+ " from person where id = 1",
+ into(stInner));
+stInner.exchange(into(name));
+stOuter.execute();
+stOuter.fetch();
+
+while (stInner.fetch())
+{
+ std::cout << name << '\n';
+}
+
+
+Stored Procedures
+
+procedure class.Acessing the native database API
+
+get_backend() functions, as described in the Beyond SOCI documentation.
+
+
+
+
+
+
+ Accessor Function
+ Concrete Class
+
+
+
+ session_backend * session::get_backend()
+ oracle_session_backend
+
+
+ statement_backend * statement::get_backend()
+ oracle_statement_backend
+
+
+ blob_backend * blob::get_backend()
+ oracle_blob_backend
+
+
+
+ rowid_backend * rowid::get_backend()
+ oracle_rowid_backendBackend-specific extensions
+eracle=soci_error
+
+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()
+{
+ try
+ {
+ // regular code
+ }
+ catch (oracle_soci_error const & e)
+ {
+ cerr << "Oracle error: " << e.err_num_
+ << " " << e.what() << endl;
+ }
+ catch (exception const &e)
+ {
+ cerr << "Some other error: " << e.what() << endl;
+ }
+}
+
+
+PostgreSQL Backend Reference
+
+
+
+Prerequisites
+Supported Versions
+
+Tested Platforms
+
+
+
+
+
+
+PostgreSQL version Operating System Compiler
+9.0 Mac OS X 10.6.6 g++ 4.2
+8.4 FreeBSD 8.2 g++ 4.1
+8.4 Debian 6 g++ 4.3
+
+8.4 RedHat 5 g++ 4.3 Required Client Libraries
+
+libpq client library.libdl, so the minimum set of libraries needed to compile a basic client program is:
+-lsoci_core -lsoci_postgresql -ldl -lpq
+
+
+Connecting to the Database
+
+session object
+using the postgresql backend factory together with a connection string:
+session sql(postgresql, "dbname=mydatabase");
+
+// or:
+session sql("postgresql", "dbname=mydatabase");
+
+// or:
+session sql("postgresql://dbname=mydatabase");
+
+
+PQconnectdb function from the libpq library.session object as shown above, you can use it to access the database, for example:
+int count;
+sql << "select count(*) from invoices", into(count);
+
+
+session class.)SOCI Feature Support
+
+Dynamic Binding
+
+row class, which facilitates retrieval of data whose type is not known at compile time.row::get<T>(), the type you should pass as T depends upon the underlying database type.
For the PostgreSQL backend, this type mapping is:
+
+
+
+
+
+ PostgreSQL Data Type
+ SOCI Data Type
+
+ row::get<T> specializations
+
+ numeric, real, double
+
+ dt_double
+ double
+
+ boolean, smallint, integer
+
+ dt_integer
+ int
+
+ int8
+
+ dt_long_long
+ long long
+
+ oid
+
+ dt_integer
+ unsigned long
+
+ char, varchar, text, cstring, bpchar
+
+ dt_string
+ std::string
+
+
+abstime, reltime, date, time, timestamp, timestamptz, timetz
+
+ dt_date
+ std::tmrow class.)Binding by Name
+
+use() function:
+int id = 7;
+sql << "select name from person where id = :id", use(id, "id")
+
+
+
+int i = 7;
+int j = 8;
+sql << "insert into t(x, y) values($1, $2)", use(i), use(j);
+
+
+SOCI_POSTGRESQL_NOBINDBYNAME and it is actually necessary for PostgreSQL 7.3, in which case binding of use elements is not supported at all. See the Configuration options section for details.Bulk Operations
+
+Transactions
+
+blob Data Type
+
+blob class with the exception that trimming is not supported.rowid Data Type
+
+Nested Statements
+
+Stored Procedures
+
+Acessing the native database API
+
+get_backend() functions, as described in the beyond SOCI documentation.
+
+
+
+
+
+
+ Accessor Function
+ Concrete Class
+
+
+
+ session_backend * session::get_backend()
+ postgresql_session_backend
+
+
+ statement_backend * statement::get_backend()
+ postgresql_statement_backend
+
+
+ blob_backend * blob::get_backend()
+ postgresql_blob_backend
+
+
+
+ rowid_backend * rowid::get_backend()
+ postgresql_rowid_backendBackend-specific extensions
+
+Configuration options
+
+
+
+
+
+SOCI_POSTGRESQL_NOBINDBYNAME - switches off the query rewriting.SOCI_POSTGRESQL_NOPARAMS - disables support for parameterized queries (binding of use elements), automatically imposes also the SOCI_POSTGRESQL_NOBINDBYNAME macro. It is necessary for PostgreSQL 7.3.SOCI_POSTGRESQL_NOPREPARE - disables support for separate query preparation, which in this backend is significant only in terms of optimization. It is necessary for PostgreSQL 7.3 and 7.4.SQLite3 Backend Reference
+
+
+
+Prerequisites
+Supported Versions
+
+Tested Platforms
+
+
+
+
+
+
+SQLite3 version Operating System Compiler
+3.5.2 Mac OS X 10.5 g++ 4.0.1
+3.1.3 Mac OS X 10.4 g++ 4.0.1
+3.2.1 Linux i686 2.6.10-gentoo-r6 g++ 3.4.5
+3.3.4 Ubuntu 5.1 g++ 4.0.2
+3.3.4 Windows XP (cygwin) g++ 3.3.4
+3.3.4 Windows XP Visual C++ 2005 Express Edition
+3.3.8 Windows XP Visual C++ 2005 Professional
+3.4.0 Windows XP (cygwin) g++ 3.4.4
+
+3.4.0 Windows XP Visual C++ 2005 Express Edition Required Client Libraries
+
+libsqlite3 client library.Connecting to the Database
+
+SQLite3 backend factory together with the database file name:
+session sql(sqlite3, "database_filename");
+
+
+session object as shown above, you can use it to access the database, for example:
+int count;
+sql << "select count(*) from invoices", into(count);
+
+
+session class.)SOCI Feature Support
+Dynamic Binding
+
+row class, which facilitates retrieval of data whose type is not known at compile time.row::get<T>(), the type you should pass as T depends upon the underlying database type.
+
+
+
+
+
+ SQLite3 Data Type
+ SOCI Data Type
+
+ row::get<T> specializations
+
+ *float*
+
+ dt_ouble
+ double
+
+ *int*
+
+ dt_integer
+ int
+
+ *char*
+
+ dt_string
+ std::string
+
+
+*date*, *time*
+
+ dt_date
+ std::tmrow class.)Binding by Name
+
+use() function:
+int id = 7;
+sql << "select name from person where id = :id", use(id, "id")
+
+
+
+int i = 7;
+int j = 8;
+sql << "insert into t(x, y) values(?, ?)", use(i), use(j);
+
+
+Bulk Operations
+
+Transactions
+
+BLOB Data Type
+
+RowID Data Type
+
+Nested Statements
+
+Stored Procedures
+
+Acessing the native database API
+
+get_backend() functions, as described in the
+beyond SOCI documentation.
+
+
+
+
+
+
+ Accessor Function
+ Concrete Class
+
+
+
+ session_backend* session::get_backend()
+ sqlie3_session_backend
+
+
+ statement_backend* statement::get_backend()
+ sqlite3_statement_backend
+
+
+
+ rowid_backend* rowid::get_backend()
+ sqlite3_rowid_backendBackend-specific extensions
+
+Configuration options
+
+Beyond standard SQL
+
+Getting the number of rows affected by an operation
+
+statement::get_affected_rows()
+method allowing to do this:
+statement st = (sql.prepare << "update some_table ...");
+st.execute(true);
+
+if ( !st.get_affected_rows() )
+{
+ ... investigate why no rows were modified ...
+}
+
+
+Working with sequences
+
+session::get_next_sequence_value() and
+session::get_last_insert_id.
+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 ...
+}
+
+
+Beyond SOCI API
+
+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);
+
+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_);
+
+
+blob object and uses two calls
+to the get_back_end function (on both the session
+and the blob objects) to get access to the actual backend
+objects. Assuming that it is the "oracle" backend which
+is in use, the downcasts allow to access all relevant low-level handles
+and use them in the call
+to the OCILobDisableBuffering function. This way, the
+BLOB handle was configured in a way that the SOCI library alone would
+not allow.
+rowid rid(sql); // sql is a session object
+sql << "select oid from mytable where id = 7", into(rid);
+
+postgresql_rowid_back_end * rbe = static_cast<postgresql_rowid_back_end *>(rid.get_back_end());
+
+unsigned long oid = rbe->value_;
+
+
+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.#include
+the appropriate backend's header file.
+
+
+
+
+
+ Previous (Interfaces)
+
+
+ Next (Client reference)
+
+ Integration with Boost
+
+boost::optional<T>
+
+boost::optional<T> provides an alternative way to support the null data condition and as such relieves the user from necessity to handle separate indicator values.boost::optional<T> objects can be used everywhere where the regular user provided
+values are expected.
+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
+}
+
+
+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<T1, ...>
+
+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;
+
+sql << "select name, phone, salary from persons where ...",
+ into(person);
+
+
+into and use elements. They can be used with rowset as well.boost::optional<T>:
+boost::tuple<string, boost::optional<string>, int> person;
+
+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
+}
+
+
+boost::fusion::vector<T1, ...>
+
+boost::fusion::vector types are supported in the same way as tuples.boost::gregorian::date
+
+boost::gregorian::date is provided as a conversion for base type std::tm and can be used as a replacement for it.
+#include <boost-optional.h>
+#include <boost-tuple.h>
+#include <boost-fusion.h>
+#include <boost-gregorian-date.h>
+
+SOCI_USE_BOOST macro before including the soci.h main header file. Note that in this case the support for boost::fusion::vector is enabled only if the detected Boost version is at least 1.35.
+
+
+
+
+
+ Previous (Multithreading)
+
+
+ Next (Interfaces)
+
+ Connections and simple queries
+
+Connecting to the database
+
+session class encapsulates the database connection
+and other backend-related details, which are common to all the
+statements
+that will be later executed. It has a couple of overloaded constructors.
+session sql(oracle, "service=orcl user=scott password=tiger");
+
+
+
+session sql(postgresql, "dbname=mydb");
+
+
+sql object is a local (automatic) object
+that encapsulates the connection.session constructor either connects successfully, or
+throws an exception.
+session sql("postgresql", "dbname=mydb");
+
+
+
+session sql("postgresql://dbname=mydb");
+
+
+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.connection_parameters type which contains a pointer to the backend to use, the connection string and also any connection options. Using this constructor is the only way to pass any non-default options to the backend. 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);
+
+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.
+
+SOCI_BACKENDS_PATH environment variable defines the set of paths where the shared libraries will be searched for. There can be many paths, separated by colons, and they are used from left to right until the library with the appropriate name is found. If this variable is not set or is empty, the current directory is used as a default path for dynamically loaded backends.register_factory_name, where
+name is a backend name. Thus:
+extern "C" void register_factory_postgresql();
+// ...
+register_factory_postgresql();
+session sql("postgresql://dbname=mydb");
+
+
+
+session sql;
+
+// some time later:
+sql.open(postgresql, "dbname=mydb");
+
+// or:
+sql.open("postgresql://dbname=mydb");
+
+// or also:
+connection_parameters parameters("postgresql", "dbname=mydb");
+sql.open(parameters);
+
+
+closed and reconnected, which can help with basic session error recovery. The reconnect function has no parameters and attempts to use the same values as those provided with earlier constructor or open calls.sessions at the same
+time, even using different backends.
+
+mysql (requires #include "soci-mysql.h")oracle (requires #include "soci-oracle.h")postgresql (requires #include "soci-postgresql.h")
+
+
+
+
+
+ Previous (Errors)
+
+
+ Next (Queries)
+
+ Errors
+
+soci_error,
+which is derived from std::runtime_error.
+This allows to
+handle database errors within the standard exception framework:
+int main()
+{
+ try
+ {
+ // regular code
+ }
+ catch (std::exception const & e)
+ {
+ cerr << "Bang! " << e.what() << endl;
+ }
+}
+
+
+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()
+{
+ 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;
+ }
+}
+
+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()
+{
+ 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;
+ }
+}
+
+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()
+{
+ 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;
+ }
+}
+
+
+
+
+
+
+
+ Previous (Installation)
+
+
+ Next (Connections and simple queries)
+
+ Exchanging data
+
+
+
+Binding local data
+
+Binding output data
+
+into expression is used to add binding information to
+the statement:
+int count;
+sql << "select count(*) from person", into(count);
+
+string name;
+sql << "select name from person where id = 7", into(name);
+
+
+into elements as there are
+expected columns in the result (see dynamic
+resultset binding for the exception to this rule).Binding input data
+
+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);
+
+
+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.
+int number = 7;
+sql << "insert into numbers(val) values(:blabla)", use(number);
+
+
+numbers - we say that the local variable is used in the SQL statement.use elements as there are
+parameters used in the SQL query.SOCI_POSTGRESQL_NOPARAMS preprocessor
+name passing -DSOCI_POSTGRESQL_NOPARAMS=ON variable to CMake.Binding by position
+
+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;
+
+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",
+ into(firstName), into(lastName), use(personId);
+
+
+into and use expression should
+match.Binding by name
+
+
+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");
+
+
+
+string addr = "...";
+sql << "update person"
+ " set mainaddress = :addr, contactaddress = :addr"
+ " where id = 7",
+ use(addr, "addr");
+
+
+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:
+
+string getNameFromSomewhere();
+
+sql << "insert into person(name) values(:n)",
+ use(getNameFromSomewhere());
+
+getNameFromSomewhere - this
+should be avoided and named variables should be used to ensure safe lifetime relations:
+// safe code:
+
+string getNameFromSomewhere();
+
+string name = getNameFromSomewhere();
+sql << "insert into person(name) values(:n)",
+ use(name);
+
+
+const data for use elements.
+Note that some database servers, like Oracle, allow PL/SQL procedures to modify their
+in/out parameters - this is detected by the SOCI library and an error is reported
+if the database attempts to modify the use element that holds const
+data.
+// safe and efficient code:
+
+string getNameFromSomewhere();
+
+const string & name = getNameFromSomewhere();
+sql << "insert into person(name) values(:n)",
+ use(name);
+
+$1,
+$2, $3, etc. In fact, the backend rewrites
+the given query to the native form - and this is also one of the very few places
+where SOCI intrudes into the SQL query. For portability reasons, it is
+recommended to use named parameters, as shown in the examples above.
+The query rewriting can be switched off by compiling the backend with
+the SOCI_POSTGRESQL_NOBINDBYNAME name defined (pass
+-DSOCI_POSTGRESQL_NOBINDBYNAME=ON variable to CMake).
+Note that in this case it is also necessary to define SOCI_POSTGRESQL_NOPREPARE
+(controlled by CMake variable -DSOCI_POSTGRESQL_NOPREPARE=ON),
+because statement preparation relies on successful query rewriting.
+In practice, both macros will be needed for PostgreSQL server older than 8.0.Handling nulls and other conditions
+
+Indicators
+
+
+select name from person where id = 7
+
+
+
+
+
+
+The following example does this:
+string name;
+indicator ind;
+
+sql << "select name from person where id = 7", into(name, ind);
+
+if (sql.got_data())
+{
+ 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
+}
+
+
+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.
+int id = 7;
+string name;
+indicator ind = i_null;
+sql << "insert into person(id, name) values(:id, :name)",
+ use(id), use(name, ind);
+
+
+name
+attribute set to null.
+vector<string> names(100);
+vector<indicator> inds;
+sql << "select name from person where id = 7", into(names, inds);
+
+
+names vector provides the (maximum)
+number of rows that should be read. Both vectors will be
+automatically resized according to the number of rows that were
+actually read.
+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);
+}
+
+sql << "insert into person(id, name) values(:id, :name)",
+ use(ids), use(name, nameIndicators);
+
+
+Types
+
+Static type binding
+
+into and use
+functions.into
+and use expressions:
+
+
+char (for character values)short, int, unsigned
+long, long long, double (for numeric values)char*, char[], std::string
+(for string values)std::tm (for datetime
+values)soci::statement (for nested statements and PL/SQL
+cursors)soci::blob (for Binary Large OBjects)soci::row_id (for row identifiers)Static type binding for bulk operations
+
+std::vector based into and use types:
+
+
+
+std::vector<char>std::vector<short>std::vector<int>std::vector<unsigned long>std::vector<long long>std::vector<double>std::vector<std::string>std::vector<std::tm>std::vectors of the types listed above.Dynamic resultset binding
+
+select * from ...") and format the
+resulting data based upon its type.
+SOCI supports this through the soci::row and soci::column_properties
+classes.row object, which holds column_properties
+objects describing
+the attributes of data contained in each column. Once the data type for each
+column is known, the data can be formatted appropriately.
+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)
+{
+ 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;
+}
+doc << "</row>";
+
+
+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().row::get<T>()
+throws an exception of type
+std::bad_cast if an incorrect type T is
+requested.
+
+
+
+
+
+ SOCI Data Type
+
+ row::get<T> specialization
+
+
+ dt_double
+ double
+
+
+ dt_integer
+ int
+
+
+ dt_long_long
+ long long
+
+
+ dt_unsigned_long_long
+ unsigned long long
+
+
+ dt_string
+ std::string
+
+
+
+ dt_date
+ std::tmrow 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);
+}
+
+
+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);
+
+string name, address;
+int age;
+
+r >> name >> address >> age;
+
+
+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
+(see next page).Extending SOCI to support custom (user-defined) C++ types
+
+type_conversion
+struct that converts to and from one of the following SOCI base types:
+
+
+doubleintlong longunsigned long longstd::stringcharstd::tmtype_conversion
+specialization:
+
+
+base_type type definition, aliasing either one of the base types or another ser-defined typefrom_base() static member function, converting from
+the base typeto_base() static member function, converting to the
+base typeMyInt, which here is some wrapper
+for the fundamental int type:
+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>
+ {
+ typedef int base_type;
+
+ static void from_base(int i, indicator ind, MyInt & mi)
+ {
+ if (ind == i_null)
+ {
+ throw soci_error("Null value not allowed for this type");
+ }
+
+ mi.set(i);
+ }
+
+ static void to_base(const MyInt & mi, int & i, indicator & ind)
+ {
+ i = mi.get();
+ ind = i_ok;
+ }
+ };
+}
+
+
+soci::type_conversion<MyInt> is enough
+to enable the following:
+MyInt i;
+
+sql << "select count(*) from person", into(i);
+
+cout << "We have " << i.get() << " persons in the database.\n";
+
+
+into_type<T> and use_type<T>
+class templates, which specializations can be user-provided. These
+specializations need to implement the interface defined by,
+respectively, the into_type_base and use_type_base
+classes.Object-relational mapping
+
+values specifically to
+enable object-relational mapping via type_conversion
+specializations.Person object to
+and from a
+database table containing columns "ID", "FIRST_NAME", "LAST_NAME", and
+"GENDER".Person object
+itself does not contain any SOCI-specific code:
+struct Person
+{
+ int id;
+ std::string firstName;
+ std::string lastName;
+ std::string gender;
+};
+
+namespace soci
+{
+ template<>
+ struct type_conversion<Person>
+ {
+ typedef values base_type;
+
+ 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");
+
+ // 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;
+ }
+ };
+}
+
+
+type_conversion specialization in place, it
+is possible to use Person directly with SOCI:
+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 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);
+
+
+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.Large objects (BLOBs)
+
+
+blob b(sql); // sql is a session object
+sql << "select mp3 from mymusic where id = 123", into(b);
+
+
+blob
+interface, mimicking the file-like operations:
+
+
+std::size_t get_len();std::size_t read(std::size_t offset, char *buf, std::size_t
+toRead);std::size_t write(std::size_t offset, char const *buf,
+std::size_t toWrite);std::size_t append(char const *buf, std::size_t toWrite);void trim(std::size_t newLen);offset parameter is always counted from the beginning
+of the BLOB's data.
+
+trim function is not currently available for
+the PostgreSQL backend.
+
+
+
+
+
+ Previous (Connections and simple queries)
+
+
+ Next (Statements, procedures and transactions)
+
+ Documentation and tutorial
+
+
+
+
+#include "soci.h"
+#include "soci-oracle.h"
+#include <iostream>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <exception>
+
+using namespace soci;
+using namespace std;
+
+bool get_name(string &name)
+{
+ cout << "Enter name: ";
+ return cin >> name;
+}
+
+int main()
+{
+ 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))
+ {
+ 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';
+ }
+ }
+ }
+ catch (exception const &e)
+ {
+ cerr << "Error: " << e.what() << '\n';
+ }
+}
+
+
+
+
+
+
+
+
+
+ Next (Library structure, files and compilation)
+
+ Installation
+
+
+
+Requirements
+
+
+
+
+
+
+
+Downloading
+
+git clone git://github.com/SOCI/soci.git
+
+Building using CMake
+
+-DVARIABLE:TYPE=VALUE. If you are new to CMake, you may find the tutorial Running CMake helpful.
+
+
+
+
+CMAKE_BUILD_TYPE
+ string
+ Specifies the build type for make based generators (see CMake help).
+
+
+CMAKE_INSTALL_PREFIX
+ path
+ Install directory used by install command (see CMake help).
+
+
+CMAKE_VERBOSE_MAKEFILE
+ boolean
+ If ON, create verbose makefile (see CMake help).
+
+
+
+
+
+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. If ON, CMake will try to find Boost headers and binaries of Boost.Date_Time 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 backend. Automatically switched on, if
+WITH_DB2 is set to ON.
+
+SOCI_DB2_TEST_CONNSTR
+ string
+ See DB2 backend refernece 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 backend. Automatically switched on, if
+WITH_FIREBIRD is set to ON.
+
+SOCI_FIREBIRD_TEST_CONNSTR
+ string
+ See Firebird backend refernece 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 libraries providing MySQL C API. Note, currently the mysql_config 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 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. 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 or iODBC 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 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 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) 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 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 refernece. 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 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 backend. Automatically switched on, if
+WITH_POSTGRESQL is set to ON.
+
+SOCI_POSTGRESQL_TEST_CONNSTR
+ boolean
+ Connection string to PostgreSQL test database. Format of the string is explained PostgreSQL backend refernece. Example:
+-DSOCI_POSTGRESQL_TEST_CONNSTR:STRING="dbname=mydb user=mloskot"SQLite 3
+
+
+
+
+
+
+WITH_SQLITE3
+ boolean
+ Should CMak try to detect SQLite C/C++ library. As bonus, the configuration tries OSGeo4W distribution 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 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 refernece for details. Example:
+-DSOCI_SQLITE3_TEST_CONNSTR="my.db"Empty (sample backend)
+
+
+
+
+
+
+SOCI_EMPTY
+ boolean
+ Builds the sample backend 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"Building using CMake on Unix
+
+
+$ mkdir build
+$ cd build
+$ cmake -G "Unix Makefiles" -DWITH_BOOST=OFF -DWITH_ORACLE=OFF (...) ../soci-X.Y.Z
+$ make
+$ make install
+
+
+Building using CMake on Windows
+
+
+C:\>MKDIR build
+C:\>cd build
+C:\build>cmake -G "Visual Studio 10" -DWITH_BOOST=OFF -DWITH_ORACLE=OFF (...) ..\soci-X.Y.Z
+C:\build>msbuild.exe SOCI.sln
+
+
+Building using classic Makefiles on Unix (deprecated)
+
+
+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.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/name directory contains the
+backend part for each supported backend with the appropriate Makefile.basic
+and the backends/name/test
+directory contains the test program for the given backend.
+$ cd src/core
+$ make -f Makefile.basic
+$ cd ../backends/postgresql
+$ make -f Makefile.basic
+$ cd test
+$ make -f Makefile.basic
+
+
+Makefile.basics
+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.basics.
+Please review their values in case of any compilation problems.Running regression tests
+
+SOCI_TESTS=ON to enable building regression tests. Also, specify SOCI_{backend name}_TEST_CONNSTR variables to tell the tests runner how to connect with your test databases.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
+
+
+make test target.Libraries usage
+
+build/lib directory
+will consist of the static libraries named like libsoci_core.a, libsoci_sqlite3.a and shared libraries with names like
+libsoci_core.so.3.2.2, libsoci_sqlite3.so.3.2.2, and so on.
| + Previous (Structure) + | ++ Next (Errors) + | +
Copyright © 2010-2013 Mateusz Loskot
+Copyright © 2004-2011 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/interfaces.html b/doc/interfaces.html new file mode 100644 index 0000000000..72e168ace9 --- /dev/null +++ b/doc/interfaces.html @@ -0,0 +1,103 @@ + + + + + +One of the major features of SOCI, although not immediately visible, is the variety of interfaces (APIs) that are available for the user. These can be divided into sugar, core and simple.
+ +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");
+
+int id = 123;
+string name;
+
+sql << "select name from persons where id = :id", into(name), use(id);
+
+
+The above example is equivalent to the following, more explicit sequence of calls:
+
+session sql("postgresql://dbname=mydb");
+
+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);
+
+
+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.
+ +Note that the sugar interface wraps only those parts of the core that are related to data binding and query streaming.
+ +The simple interface is provided specifically to allow easy integration of the SOCI library with other languages that have the ability to link with binaries using the "C" calling convention. To facilitate this integration, the simple interface does not use any pointers to data except C-style strings and opaque handles, but the consequence of this is that user data is managed by SOCI and not by user code. To avoid exceptions passing the module boundaries, all errors are reported as state variables of relevant objects.
+ +The above examples can be rewritten as (without error-handling):
+ +
+#include <soci-simple.h>
+
+// ...
+session_handle sql = soci_create_session("postgresql://dbname=mydb");
+
+statement_handle st = soci_create_statement(sql);
+
+soci_use_int(st, "id");
+soci_set_use_int(st, "id", 123);
+
+int namePosition = soci_into_string(st);
+
+soci_prepare(st, "select name from persons where id = :id");
+
+soci_execute(st, true);
+
+char const * name = soci_get_into_string(st, namePosition);
+
+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 documentation for more details.
+ +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.
+ +| + Previous (Boost) + | ++ Next (Beyond standard SQL) + | +
Copyright © 2012 Vadim Zeitlin
+Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/languages/ada/concepts.html b/doc/languages/ada/concepts.html new file mode 100644 index 0000000000..4880c5597a --- /dev/null +++ b/doc/languages/ada/concepts.html @@ -0,0 +1,64 @@ + + + + +Concepts
++The SOCI-Ada library borrows its concepts and naming from the main SOCI project. They are shortly explained here in the bottom-up fashion. +
++One of the main properties of the library is that the data objects which are bound for transfer to and from the database server are managed by the library itself and are not directly visible from the user code. This ensures that no aliasing of objects occurs between Ada and underlying C++ code, which makes the inter-language interface easier and more resilient to the differences in how compilers handle the linkage. As a direct result of this design choice, users of SOCI-Ada need to instruct the library to internally create all objects that will be subject to data transfer. +
++There are two kinds of objects that can be managed by the SOCI-Ada library: +
++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: +
+StringSOCI.DB_Integer, which is defined by the library in terms of Interfaces.C.intSOCI.DB_Long_Long_Integer, which is defined in terms of Interfaces.Integer_64SOCI.DB_Long_Float, which is defined in terms of Interfaces.C.doubleAda.Calendar.Time+Both into and use elements are managed for a single statement, which can be prepared once and executed once or many times, with data transfer handled during execution or fetch phase. +
++Statements can be managed explicitly, which is required if they are to be used repeteadly or when data transfer is needed or implicitly, which is a shorthand notation that is particularly useful with simple queries or DDL commands. +
++All statements are handled within the context of some session, which also supports transactions. +
++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. +
+ +| + Back to index + | ++ Common Idioms + | +
Copyright © 2008-2011 Maciej Sobczak
+ + diff --git a/doc/languages/ada/idioms.html b/doc/languages/ada/idioms.html new file mode 100644 index 0000000000..07452d3812 --- /dev/null +++ b/doc/languages/ada/idioms.html @@ -0,0 +1,306 @@ + + + + +Common 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. +
++The idioms below are provided as complete programs with the intent to make them more understandable and to give complete context of use for each idiom. The programs assume that the target database is PostgreSQL, but this can be changed by a different connection string in each place where the sessions are established. The programs use the Ada 2005 interface and some minor changes will be required to adapt them for Ada 95 compilers. +
+ +Single query without data transfer
++This type of query is useful for DDL commands and can be executed directly on the given session, without explicit statement management. +
+
+with SOCI;
+
+procedure My_Program is
+
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+
+begin
+
+ SQL.Execute ("drop table some_table");
+
+end My_Program;
+
+
+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. +
+
+with SOCI;
+with Ada.Text_IO;
+
+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;
+
+ Num_Of_Persons : SOCI.DB_Integer;
+
+begin
+
+ Pos := St.Into_Integer;
+ St.Prepare ("select count(*) from persons");
+ St.Execute (True);
+
+ Num_Of_Persons := St.Get_Into_Integer (Pos);
+
+ Ada.Text_IO.Put_Line ("Number of persons: " & SOCI.DB_Integer'Image (Num_Of_Persons));
+
+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;
+
+procedure My_Program is
+
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
+
+begin
+
+ St.Use_Integer ("increase");
+ St.Set_Use_Integer ("increase", 1000);
+
+ St.Prepare ("update persons set salary = salary + :increase");
+ St.Execute (True);
+
+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. +
+
+with SOCI;
+
+procedure My_Program is
+
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
+
+begin
+
+ St.Use_String ("name");
+
+ St.Prepare ("insert into countries(country_name) values(:name)");
+
+ St.Set_Use_String ("name", "Poland");
+ St.Execute (True);
+
+ St.Set_Use_String ("name", "Switzerland");
+ St.Execute (True);
+
+ St.Set_Use_String ("name", "France");
+ St.Execute (True);
+
+end My_Program;
+
+
+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;
+
+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;
+
+ use type SOCI.Vector_Index;
+
+begin
+
+ St.Use_Vector_String ("name");
+
+ St.Use_Vectors_Resize (3);
+
+ 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.Prepare ("insert into countries(country_name) values(:name)");
+ St.Execute (True);
+
+end My_Program;
+
+
+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. +
+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.
+
Simple query with many rows of results
++This type of query requires simple into elements. +
+
+with SOCI;
+with Ada.Text_IO;
+
+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;
+
+begin
+
+ Pos := St.Into_String;
+
+ St.Prepare ("select country_name from countries");
+ St.Execute;
+
+ while St.Fetch loop
+
+ Ada.Text_IO.Put_Line (St.Get_Into_String (Pos));
+
+ end loop;
+
+end My_Program;
+
+
+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. +
+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;
+
+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;
+
+ Batch_Size : constant := 10;
+
+begin
+
+ Pos := St.Into_Vector_String;
+ St.Into_Vectors_Resize (Batch_Size);
+
+ St.Prepare ("select country_name from countries");
+ St.Execute;
+
+ while St.Fetch 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));
+
+ end loop;
+
+ St.Into_Vectors_Resize (Batch_Size);
+
+ end loop;
+
+end My_Program;
+
+
+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.
+
+There is a tradeoff between efficiency and memory usage and this tradeoff is controlled by the requested batch size. Similarly to one of the examples above, there is no benefit from using batches bigger than tens of thousands of rows. +
++This type of query can have simple (not vectors) parameters that are fixed at execution time. +
+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. +
+| + Concepts + | ++ API Reference + | +
Copyright © 2008-2011 Maciej Sobczak
+ + diff --git a/doc/languages/ada/index.html b/doc/languages/ada/index.html new file mode 100644 index 0000000000..dfa859b7c0 --- /dev/null +++ b/doc/languages/ada/index.html @@ -0,0 +1,78 @@ + + + + +
+Introduction
+Compilation
+Concepts
+Common Idioms
+API Reference
+
+SOCI-Ada is a database access library for Ada. +
++The library itself is a wrapper for the selected functionality of the SOCI library, which is a C++ database access library recognized for its high quality and innovative interface. +
++The SOCI-Ada library offers the following features to the Ada community: +
++Currently the following database servers are directly supported via their native interfaces: +
++Other backends exist in the SOCI Git repository and can be provided with future version of the library. +
+ + ++In order to use SOCI-Ada, compile the C++ parts first (core and required backends). +
++Note: SOCI header files are not needed to use SOCI-Ada, only compiled SOCI libraries (core and relevant backend) need to exist to build and use SOCI-Ada programs. +
+
+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.
+
| + | ++ Concepts + | +
Copyright © 2008-2011 Maciej Sobczak
+ + diff --git a/doc/languages/ada/reference.html b/doc/languages/ada/reference.html new file mode 100644 index 0000000000..42dc415bac --- /dev/null +++ b/doc/languages/ada/reference.html @@ -0,0 +1,674 @@ + + + + +API Reference
+
+The SOCI-Ada library is entirely implemented as a single package named
+SOCI. Additional child packages contain single procedures
+for static registration of backends - these child packages are not
+necessary for typical use, but can be useful to force static
+linking of backend code.
+
+The following describes all publicly visible elements of this package: +
+ ++ -- + -- General exception related to database and library usage. + -- + + 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. + -- + + type Session is tagged limited private; + + 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 Close (This : in out Session); + + 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:
+
+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.
+
+ -- Transaction management. + + not overriding + procedure Start (This : in Session); + + not overriding + procedure Commit (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); ++
+This operation allows to create implicit statement, prepare it for the given Query and execute it.
+
+ -- + -- Connection pool management. + -- + + 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 Close (This : in out Connection_Pool; Position : in Positive); + + 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 Opened and Closed 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:
+
+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 Sessions.
+
+ -- + -- Statement. + -- + + type Statement (<>) is tagged limited private; + + type Data_State is (Data_Null, Data_Not_Null); + + type Into_Position is private; + + 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.
+
+Into_Position is used to identify into elements. Vector_Index is used to name individual entries in vector into and use elements.
+
+ not overriding + function Make_Statement (Sess : in Session'Class) return Statement; + + -- Ada 95 version: + -- procedure Make_Statement (This : in out Statement; Sess : in Session'Class); ++
+Construction function for creating Statement objects. The Statement is associated with one Session for its whole lifetime.
+
+ -- Statement preparation and execution. + + 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 + 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 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.
+
+The Execute operations cause the statement to execute, which might be combined with data exchange if requested. The function version of this operation returns True if some data has been returned back from the database server.
+
+The Fetch function is used to transfer next portion of data (a single row or a whole bunch) from the database server and returns True if some data has been fetched. If this function returns False it means that no new data will be ever fetched for this statement and indicates the end-of-row condition.
+
+The Got_Data function returns True if the last execution or fetch resulted in some data being transmitted from the database server.
+
+ -- + -- 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. + + 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: +
+StringDB_Integer, defined aboveDB_Long_Long_Integer, defined aboveDB_Long_Float, defined aboveAda.Calendar.Time+ -- Creation of single into elements. + + 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_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; ++
+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:
++Simple into elements cannot be created together with vector into elements for the same statement. +
++ -- Creation of vector into elements. + + not overriding + function Into_Vector_String (This : in Statement) return Into_Position; + + not overriding + function Into_Vector_Integer (This : in Statement) return Into_Position; + + not overriding + function Into_Vector_Long_Long_Integer (This : in Statement) return Into_Position; + + not overriding + function Into_Vector_Long_Float (This : in Statement) return Into_Position; + + not overriding + function Into_Vector_Time (This : in Statement) return Into_Position; ++
+These functions instruct the library to create internal vector into elements of the relevant type. They return the position of the into element, which can be later used to identify it. +
+ +Note:
++Vector into elements are empty (they have size 0) after they are created and have to be resized before any data is written to them. +
+Note:
++Simple into elements cannot be created together with vector into elements for the same statement. +
++ -- Inspection of single into elements. + + 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; + + 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; ++
+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. + + 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_Last_Index (This : in Statement) return Vector_Index; + + 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.
+
+The Into_Vectors_First_Index returns the lowest index value for vector into elements (which is always 0, even if the vectors are empty). The Into_Vectors_Last_Index returns the last index of into vectors, and raises the CONSTRAINT_ERROR exception if the vectors are empty.
+
+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; + + 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_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_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.
+
+ -- Creation of single use elements. + + 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_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_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. +
++Vector use elements cannot be created together with any into elements for the same statement. +
++ -- Creation of vector use elements. + + not overriding + procedure Use_Vector_String (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); ++
+These functions instruct the library to create internal vector 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. +
++Vector use elements cannot be created together with any into elements for the same statement. +
++ -- Modifiers for single use elements. + + 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); + + 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); ++
+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.
+
+ -- Modifiers for vector use elements. + + 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_Last_Index (This : in Statement) return Vector_Index; + + 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.
+
+The Use_Vectors_First_Index returns the lowest index value for vector use elements (which is always 0, even if the vectors are empty). The Use_Vectors_Last_Index returns the last index of use vectors, and raises the CONSTRAINT_ERROR exception if the vectors are empty.
+
+The Use_Vectors_Resize procedure allows to change the size of all use vectors for the given statement.
+
+ 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_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_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.
+
+ -- 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_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_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_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.
+
| + Idioms + | ++ | +
Copyright © 2008-2011 Maciej Sobczak
+ + diff --git a/doc/languages/ada/style.css b/doc/languages/ada/style.css new file mode 100644 index 0000000000..7f6dc8ca6f --- /dev/null +++ b/doc/languages/ada/style.css @@ -0,0 +1,67 @@ +body +{ + background-color: white; + color: black; + margin-left: 100px; + margin-right: 100px; + font-family: arial, sans-serif; + font-size: small; +} + +div.note +{ + border-color: black; + border-style: dotted; + border-width: 1px; + margin-top: 20px; + padding-left: 20px; + padding-right: 20px; +} + +span.note +{ + font-size: large; + font-weight: bold; +} + +code +{ +/* font-weight: bold; */ + background-color: white; + color: #8B0000; +/* font-size: large; */ +} + +pre.example +{ + background-color: #F0F0F0; + color: black; + border-width: 1px; + border-style: dashed; + border-color: blue; + padding: 10px 10px 10px 10px; +} + +table.foot-links +{ + width: 100%; + padding-top: 20px; +} + +td.foot-link-left +{ + text-align: left; +} + +td.foot-link-right +{ + text-align: right; +} + +p.copyright +{ + border-top-color: black; + border-top-style: solid; + border-top-width: 1px; + padding-top: 5px; +} diff --git a/doc/multithreading.html b/doc/multithreading.html new file mode 100644 index 0000000000..a22a742ecc --- /dev/null +++ b/doc/multithreading.html @@ -0,0 +1,64 @@ + + + + + +The general rule for multithreading is that SOCI classes are not thread-safe, meaning that their instances should not be used concurrently by multiple threads.
+ +The simplest solution for multithreaded code is to set up a separate session object for each thread that needs to inteact with the database. Depending on the design of the client application this might be also the most straightforward approach.
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:
+// phase 1: preparation
+
+const size_t poolSize = 10;
+connection_pool pool(poolSize);
+
+for (size_t i = 0; i != poolSize; ++i)
+{
+ session & sql = pool.at(i);
+
+ sql.open("postgresql://dbname=mydb");
+}
+
+// phase 2: usage from working threads
+
+{
+ session sql(pool);
+
+ 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 sessions in the disconnected state. Later, the at function provides non-synchronized access to each element of the array. Note that this function is not thread-safe and exists only to make it easier to set up the pool in the initialization phase.
Note that it is not obligatory to use the same connection parameters for all sessions in the pool, although this will be most likely the usual case.
+ +The working threads that need to lease a single session from the pool use the dedicated constructor of the session class - this constructor blocks until some session object becomes available in the pool and attaches to it, so that all further uses will be forwarded to the session object managed by the pool. As long as the local session object exists, the associated session in the pool is locked and no other thread will gain access to it. When the local session variable goes out of scope, the related entry in the pool's internal array is released, so that it can be used by other threads. This way, the connection pool guarantees that its session objects are never used by more than one thread at a time.
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 for details.
| + Previous (Statements, procedures and transactions) + | ++ Next (Boost) + | +
Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/queries.html b/doc/queries.html new file mode 100644 index 0000000000..b8afd4725d --- /dev/null +++ b/doc/queries.html @@ -0,0 +1,146 @@ + + + + + +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"; ++ +
For shorter syntax, the following form is also allowed:
+ ++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<<):
+string tableName = "persons"; +sql << "drop table " << tableName; + +int id = 123; +sql << "delete from companies where id = " << id; ++ +
In SOCI 3.2.0, query transformation mechanism was introduced.
+ +Query transformation is specified as user-defined unary function or callable
+function object with input parameter of type std::string which
+returns object of type std::string as well.
The query transformation function is registered for current database session
+using dedicated session::set_query_transformation method.
+Then, the transformation function is called with query string as argument
+just before the query is sent to database backend for execution or for
+preparation.
For one-time statements, query transformation is performed before +each execution of statement. For prepared statements, query is transformed +only once, before preparation, regardless how many times it is executed.
+ +A few short examples how to use query transformation:
+ +
+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";
+
+
+
+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;
+
+
+
+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.
+ +For example, the query transformation may be used to:
+WHERE
+ clause with new condition)SELECT /*+RULE*/ A FROM C in Oracle 9)Query transformation mechanism can also be considered for similar uses as
+prefix_with function from
+SQLAlchemy Expressions API.
| + Previous (Connections) + | ++ Next (Exchanging data) + | +
Copyright © 2013 Mateusz Loskot
+Copyright © 2004-2008 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/rationale.html b/doc/rationale.html new file mode 100644 index 0000000000..deb5faab0e --- /dev/null +++ b/doc/rationale.html @@ -0,0 +1,313 @@ + + + + + +This part of the documentation is supposed to gather in a single place +the usual questions (and answers) about SOCI with regard to the design +decisions that have shaped it.
+ +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".
+ +Still, Oracle is considered to be the main driving server technology in +terms of the set of features that are supported by the library. This +also means that backends for other servers might need to +work around some of the imposed idioms and protocols, but already +available and well-working PostgreSQL, MySQL and SQLite3 backends show +that it's actually not that +bad and the abstractions provided by the library are actually very +universal. Of +course, some of the features that were provided for Oracle might not be +supported for all other servers, but we think that it's better to have +one leading technology (where at least one group is fully happy) +instead of some "common denominator" for all databases (where nobody is happy).
+ +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);
+ /* ... */
+}
+
+
+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, specialized 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); ++ +
(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)
+ +Apart from mimicking the Embedded SQL techniques in the regular, fully +standard C++ code, the above syntax has the following benefit: it is +minimal with respect to what has to +be said. Every single piece above is needed and expresses something +important, like:
+sql object
+encapsulates the session,std::string variable,a
+will receive the result.Everything else is just a couple of operators that allow to treat the +whole as a single expression. It's rather difficult to remove anything +from this example.
+ +The fact that the basic SOCI syntax is minimal (but without being +obscure at the same time, see below) means that the programmer does not +need to bother with unnecessary noise that some other database +libraries impose. We hope that after having written one line of code +like above by themselves, most programmers will react with something +like "how obvious!" instead of "how advanced!".
+ +First, you don't need to use SQL queries as string literals. In bigger +projects it is a common practice to store SQL queries externally (in a +file, or in a... database) and load them before use. This means that +they are not necessarily expected to appear in the program code, as +they do in our simple code examples and the advantage of separating +them from the source code of the main program is, among others, the +possibility to optimize and tune the SQL queries without recompilation +and relinking of the whole program.
+ +What is the most important, though, is that SOCI does not try to mess +with the +text of the query (apart from very few cases), which means that the +database server will get exactly the same text of the query as is used +in the program. The advantage of this is that there is no new SQL-like +(or even SQL-unlike) +syntax that you would need to learn, and also that it's much easier to +convince a typical +DBA to help with SQL tuning or other specialized activities, if he is +given the material in the form that is not polluted with any foreign +abstractions.
+ +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");
+
+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
+
+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.
+ +Having said that, we have provided some form of the stream-like +interface, but with the important limitation that the stream is always bound to the +single row, so that the row-tearing effect is not possible. In other words, +data returned from the database is still structured into rows, but each row can be +separately traversed like a stream. We hope that it provides a good balance between +convenience and code safety.
+ +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.
+ +Having said that, it is important to point at the Integration with Boost
+that allows to use boost::optional<T> to conveniently pack together
+the data and the information about its state.
Well, consider the following:
+ +"Send the query X to the server Y and put result into variable Z."
+ +Above, the "and" plays a role of the comma. Even if overloading the +comma operator is not a very popular practice in C++, some libraries do +this, achieving terse and easy to learn syntax. We are pretty sure that +in SOCI the comma operator was overloaded with a good effect.
+ +operator<< provides a bad abstraction for the "input" statements.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.
We decided to use the Boost license, 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 plain +uncertainty) 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 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 +on the relevant SOCI page) to discuss any remaining concerns.
+ +| + Previous (Backends reference) + | ++ Next (Existing backends) + | +
Copyright © 2004-2006 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/reference.html b/doc/reference.html new file mode 100644 index 0000000000..d265f75e26 --- /dev/null +++ b/doc/reference.html @@ -0,0 +1,963 @@ + + + + + +The core client interface is a set of classes and free functions declared in
+the soci.h header file. All names are dbeclared in the soci
+namespace.
There are also additional names declared in the soci::details
+namespace, but they are not supposed to be directly used by the users
+of the library and are therefore not documented here. When such types
+are used in the declarations that are part of the "public" interface,
+they are replaced by "IT", which means "internal type". Types related
+to the backend interface are named here, but documented on the next page.
The following types are commonly used in the rest of the interface:
+ +
+// data types, as seen by the user
+enum data_type { dt_string, dt_date, dt_double, dt_integer, dt_long_long, dt_unsigned_long_long };
+
+// the enum type for indicator variables
+enum indicator { i_ok, i_null, i_truncated };
+
+// the type used for reporting exceptions
+class soci_error : public std::runtime_error { /* ... */ };
+
+
+The data_type type defines the basic SOCI data types.
+User provided data types need to be associated with one of these basic
+types.
The indicator type defines the possible states of data.
The soci_error type is used for error reporting.
The session class encapsulates the connection to the
+database.
+class session
+{
+public:
+ session();
+ explicit session(connection_parameters const & parameters);
+ session(backend_factory const & factory, std::string const & connectString);
+ session(std::string const & backendName, std::string const & connectString);
+ explicit session(std::string const & connectString);
+ explicit session(connection_pool & pool);
+
+ ~session();
+
+ void open(backend_factory const & factory, std::string const & connectString);
+ void open(std::string const & backendName, std::string const & connectString);
+ void open(std::string const & connectString);
+ void close();
+ void reconnect();
+
+ void begin();
+ void commit();
+ void rollback();
+
+ IT once;
+ IT prepare;
+
+ template <typename T> IT operator<<(T const & t);
+
+ bool got_data() const;
+
+ bool get_next_sequence_value(std::string const & sequence, long & value);
+ bool get_last_insert_id(std::string const & table, long & value);
+
+ std::ostringstream & get_query_stream();
+
+ void set_log_stream(std::ostream * s);
+ std::ostream * get_log_stream() const;
+
+ std::string get_last_query() const;
+
+ void uppercase_column_names(bool forceToUpper);
+
+ details::session_backend * get_backend();
+
+ std::string get_backend_name() const;
+};
+
+
+This class contains the following members:
+
+session sql(postgresql, "dbname=mydb");
+session sql("postgresql", "dbname=mydb");
+session sql("postgresql://dbname=mydb");
+
+ The constructors that take backend name as string load the shared library (if not yet loaded)
+ with name computed as libsoci_ABC.so (or libsoci_ABC.dll on Windows)
+ where ABC is the given backend name.
+ open, close and reconnect functions for
+ reusing the same session object many times; the reconnect function attempts
+ to establish the connection with the same parameters as most recently used with constructor
+ or open. The arguments for open are treated in the same way as
+ for constructors.
+ begin, commit and rollback
+functions for transaction control.
+ once member, which is used for performing instant
+queries that do not need to be separately prepared. Example:
++sql.once << "drop table persons"; ++
prepare member, which is used for statement
+preparation - the result of the statement preparation must be provided
+to the constructor of the statement class. Example:
++int i; +statement st = (sql.prepare << + "insert into numbers(value) values(:val)", use(i)); ++
operator<< that is a shortcut forwarder to the
+equivalent operator of the once member. Example:
++sql << "drop table persons"; ++
got_data returns true if the last executed query had non-empty result.get_next_sequence_value returns true if the next value of
+ the sequence with the specified name was generated and returned in its
+ second argument. Unless you can be sure that your program will use only
+ databases that support sequences, consider using this method in conjunction
+ with get_last_insert_id() as explained in
+ "Working with sequences" section.get_last_insert_id returns true if it could retrieve the
+ last value automatically generated by the database for an auto-incremented
+ field. Notice that although this method takes the table name, for some
+ databases, such as Microsoft SQL Server and SQLite, this value is actually
+ global, so you should attempt to retrieve it immediately after performing an
+ insertion.get_query_stream provides direct access to the stream object that is used
+ to accumulate the query text and exists in particular to allow the user to imbue specific locale
+ to this stream.set_log_stream and get_log_stream functions for setting and getting
+ the current stream object used for basic query logging. By default, it is NULL, which means no logging.
+ The string value that is actually logged into the stream is one-line verbatim copy of the query string provided by the user,
+ without including any data from the use elements. The query is logged exactly once, before the preparation step.get_last_query retrieves the text of the last used query.uppercase_column_names allows to force all column names to uppercase in dynamic row description;
+ this function is particularly useful for portability, since various database servers
+ report column names differently (some preserve case, some change it).get_backend returns the internal
+pointer to the concrete backend implementation of the session. This is
+provided for advanced users that need access to the functionality that
+is not otherwise available.get_backend_name is a convenience forwarder to the same function
+of the backend object.See Connections and simple queries for more +examples.
+ +The connection_parameters class is a simple container for the backend pointer, connection string and any other connection options. It is used together with session constructor and open() method.
+class connection_parameters
+{
+public:
+ connection_parameters();
+ connection_parameters(backend_factory const & factory, std::string const & connectString);
+ connection_parameters(std::string const & backendName, std::string const & connectString);
+ explicit connection_parameters(std::string const & fullConnectString);
+
+ void set_option(const char * name, std::string const & value);
+ bool get_option(const char * name, std::string & value) const
+};
+
+
+The methods of this class are:
+session class and specify both the backend, either as a
+ pointer to it or by name, and the connection string.set_option can be used to set the value of an option with
+ the given name. Currently all option values are strings, so if you need to
+ set a numeric option you need to convert it to a string first. If an option
+ with the given name had been already set before, its old value is
+ overwritten.The connection_pool class encapsulates the thread-safe pool of connections
+and ensures that only one thread at a time has access to any connection that it manages.
+class connection_pool
+{
+public:
+ explicit connection_pool(std::size_t size);
+ ~connection_pool();
+
+ session & at(std::size_t pos);
+
+ std::size_t lease();
+ bool try_lease(std::size_t & pos, int timeout);
+ void give_back(std::size_t pos);
+};
+
+
+The operations of the pool are:
+ +session objects in disconnected state.at function that provides direct access to any given entry
+ in the pool. This function is non-synchronized.lease function waits until some entry is available (which means
+ that it is not used) and returns the position of that entry in the pool, marking
+ it as locked.try_lease acts like lease, but allows to set up a
+ time-out (relative, in milliseconds) on waiting. Negative time-out value means no time-out.
+ Returns true if the entry was obtained, in which case its position
+ is written to the pos parametr, and false if no entry
+ was available before the time-out.give_back should be called when the entry on the given position
+ is no longer in use and can be passed to other requesting thread.Note: calls to lease and give_back are automated by the
+dedicated constructor of the session class, see above.
The class transaction can be used for associating the transaction
+with some code scope. It is a RAII wrapper for regular transaction operations that
+automatically rolls back in its destructor if the transaction was not explicitly
+committed before.
+class transaction
+{
+public:
+ explicit transaction(session & sql);
+
+ ~transaction();
+
+ void commit();
+ void rollback();
+
+private:
+ // ...
+};
+
+
+Note that objects of this class are not notified of other transaction related operations +that might be executed by user code explicitly or hidden inside SQL queries. +It is not recommended to mix different ways of managing transactions.
+ +The function into is used for binding local output data
+(in other words, it defines where the results of the query are stored).
+template <typename T> +IT into(T & t); + +template <typename T, typename T1> +IT into(T & t, T1 p1); + +template <typename T> +IT into(T & t, indicator & ind); + +template <typename T, typename T1> +IT into(T & t, indicator & ind, T1 p1); + +template <typename T> +IT into(T & t, std::vector<indicator> & ind); ++ +
Example:
+ ++int count; +sql << "select count(*) from person", into(count); ++ +
See Binding local data +for more examples.
+ +The function use is used for binding local input data (in
+other words, it defines where the parameters of the query come from).
+template <typename T> +IT use(T & t); + +template <typename T, typename T1> +IT use(T & t, T1 p1); + +template <typename T> +IT use(T & t, indicator & ind); + +template <typename T, typename T1> +IT use(T & t, indicator & ind, T1 p1); + +template <typename T> +IT use(T & t, std::vector<indicator> const & ind); + +template <typename T, typename T1> +IT use(T & t, std::vector<indicator> const & ind, T1 p1); ++ +
Example:
+ ++int val = 7; +sql << "insert into numbers(val) values(:val)", use(val); ++ +
See Binding local data +for more examples.
+ +The statement class encapsulates the prepared statement.
+class statement
+{
+public:
+ statement(session & s);
+ statement(IT const & prep);
+ ~statement();
+
+ statement(statement const & other);
+ void operator=(statement const & other);
+
+ void alloc();
+ void bind(values & v);
+ void exchange(IT const & i);
+ void exchange(IT const & u);
+ void clean_up();
+
+ void prepare(std::string const & query);
+ void define_and_bind();
+
+ bool execute(bool withDataExchange = false);
+ long long get_affected_rows();
+ bool fetch();
+
+ bool got_data() const;
+
+ void describe();
+ void set_row(row * r);
+ void exchange_for_rowset(IT const & i);
+
+ details::statement_backend * get_backend();
+};
+
+
+This class contains the following members:
+session object. This can
+be used for later query preparation. Example:
++statement stmt(sql); ++
prepare
+on the session object, see example provided above for the
+ session class.alloc function, which allocates necessary internal resources.bind function, which is used to bind the values
+object - this is used in the object-relational mapping and normally
+called automatically.into or use
+functions and are normally invoked automatically.clean_up function for cleaning up resources, normally
+called automatically.prepare function for preparing the statement for
+repeated execution.define_and_bind function for actually executing the
+registered bindings, normally called automatically.execute function for executing the statement. If its
+parameter is false then there is no data exchange with
+locally bound variables (this form should be used if later fetch
+of multiple rows is foreseen). Returns true if there was at least
+one row of data returned.get_affected_rows function returns the number of rows
+affected by the last statement. Returns -1 if it's not
+implemented by the backend being used.fetch function for retrieving the next portion of
+the result. Returns true if there was new data.got_data return true if the most recent
+execution returned any rows.describe function for extracting the type
+information for the result (note: no data is exchanged). This is normally
+called automatically and only when dynamic resultset binding is used.set_row function for associating the statement
+and row objects, normally called automatically.exchange_for_rowset as a special case for binding rowset
+objects.get_backend function that returns the internal
+pointer to
+the concrete backend implementation of the statement object. This is
+provided
+for advanced users that need access to the functionality that is not
+otherwise available.See Statement preparation and +repeated execution for example uses.
+ +Most of the functions from the statement class
+interface are called automatically, but can be also used explicitly.
+See Interfaces for the description of various way to use
+this interface.
The procedure class encapsulates the call to the stored
+procedure and is aimed for higher portability of the client code.
+class procedure
+{
+public:
+ procedure(IT const & prep);
+
+ bool execute(bool withDataExchange = false);
+ bool fetch();
+ bool got_data() const;
+};
+
+
+The constructor expects the result of using prepare
+on the session object.
See Stored procedures for +examples.
+ +The type_conversion class is a traits class that is
+supposed to be provided (specialized) by the user for defining
+conversions to and from one of the basic SOCI types.
+template <typename T>
+struct type_conversion
+{
+ typedef T base_type;
+
+ static void from_base(base_type const & in, indicator ind, T & out);
+
+ static void to_base(T const & in, base_type & out, indicator & ind);
+};
+
+
+Users are supposed to properly implement the from_base and to_base
+functions in their specializations of this template class.
See Extending +SOCI to support custom (user-defined) C++ types.
+ +The row class encapsulates the data and type information
+retrieved for the single row when the dynamic rowset binding is used.
+class row
+{
+public:
+ row();
+ ~row();
+
+ void uppercase_column_names(bool forceToUpper);
+
+ std::size_t size() const;
+
+ indicator get_indicator(std::size_t pos) const;
+ indicator get_indicator(std::string const & name) const;
+
+ column_properties const & get_properties (std::size_t pos) const;
+ column_properties const & get_properties (std::string const & name) const;
+
+ template <typename T>
+ T get(std::size_t pos) const;
+
+ template <typename T>
+ T get(std::size_t pos, T const & nullValue) const;
+
+ template <typename T>
+ T get(std::string const & name) const;
+
+ template <typename T>
+ T get(std::string const & name, T const & nullValue) const;
+
+ template <typename T>
+ row const & operator>>(T & value) const;
+
+ void skip(std::size_t num = 1) const;
+
+ void reset_get_counter() const
+};
+
+
+This class contains the following members:
+row
+variable.uppercase_column_names - see the same function in the session class.size function that returns the number of columns in
+the row.get_indicator function that returns the indicator value
+for the given column (column is specified by position - starting from 0
+- or by name).get_properties function that returns the properties
+of the column given by position (starting from 0) or by name.get functions that return the value of the column
+given by position or name. If the column contains null, then these
+functions either return the provided "default" nullValue
+or throw an exception.operator>> for convenience stream-like
+extraction interface. Subsequent calls to this function are equivalent
+to calling get with increasing position parameter,
+starting from the beginning.skip and reset_get_counter allow to change the
+order of data extraction for the above operator.See Dynamic resultset binding for +examples.
+ +The column_properties class provides the type and name
+information about the particular column in a rowset.
+class column_properties
+{
+public:
+ std::string get_name() const;
+ data_type get_data_type() const;
+};
+
+
+This class contains the following members:
+get_name function that returns the name of the column.get_data_type that returns the type of the column.See Dynamic resultset binding for +examples.
+ +The values class encapsulates the data and type
+information and is used for object-relational mapping.
+class values
+{
+public:
+ values();
+
+ void uppercase_column_names(bool forceToUpper);
+
+ indicator get_indicator(std::size_t pos) const;
+ indicator get_indicator(std::string const & name) const;
+
+ template <typename T>
+ T get(std::size_t pos) const;
+
+ template <typename T>
+ T get(std::size_t pos, T const & nullValue) const;
+
+ template <typename T>
+ T get(std::string const & name) const;
+
+ template <typename T>
+ T get(std::string const & name, T const & nullValue) const;
+
+ template <typename T>
+ values const & operator>>(T & value) const;
+
+ void skip(std::size_t num = 1) const;
+ void reset_get_counter() const;
+
+ template <typename T>
+ void set(std::string const & name, T const & value, indicator indic = i_ok);
+
+ template <typename T>
+ void set(const T & value, indicator indic = i_ok);
+
+ template <typename T>
+ values & operator<<(T const & value);
+};
+
+
+This class contains the same members as the row class (with the same meaning)
+plus:
set function for storing values in named columns or in subsequent positions.operator<< for convenience.See Object-relational mapping +for examples.
+ +The blob class encapsulates the "large object"
+functionality.
+class blob
+{
+public:
+ explicit blob(session & s);
+ ~blob();
+
+ std::size_t getLen();
+ std::size_t read(std::size_t offset, char * buf, std::size_t toRead);
+ std::size_t write(std::size_t offset, char const * buf, std::size_t toWrite);
+ std::size_t append(char const * buf, std::size_t toWrite);
+ void trim(std::size_t newLen);
+
+ details::blob_backend * get_backend();
+};
+
+
+This class contains the following members:
+blob object with the session object.get_len function that returns the size of the BLOB
+object.read function that reads the BLOB data into provided
+buffer.write function that writes the BLOB data from
+provided buffer.append function that appends to the existing BLOB
+data.trim function that truncates the existing data to
+the new length.get_backend function that returns the internal
+pointer to
+the concrete backend implementation of the BLOB object. This is
+provided
+for advanced users that need access to the functionality that is not
+otherwise available.See Large objects (BLOBs) for more +discussion.
+ +The rowid class encapsulates the "row identifier" object.
+class rowid
+{
+public:
+ explicit rowid(Session & s);
+ ~rowid();
+
+ details::rowid_backend * get_backend();
+};
+
+
+This class contains the following members:
+rowid object with the session
+object.get_backend function that returns the internal
+pointer to
+the concrete backend implementation of the rowid object.The backend_factory class provides the abstract interface
+for concrete backend factories.
+struct backend_factory
+{
+ virtual details::session_backend * make_session(
+ std::string const & connectString) const = 0;
+};
+
+
+The only member of this class is the make_session function
+that is supposed to create concrete backend implementation of the
+session object.
Objects of this type are declared by each backend and should be
+provided to the constructor of the session class.
+In simple programs users do not need to use this class directly, but
+the example use is:
+backend_factory & factory = postgresql; +std::string connectionParameters = "dbname=mydb"; + +session sql(factory, parameters); ++ +
The simple client interface is provided with other languages in mind, +to allow easy integration of the SOCI library with script interpreters and those +languages that have the ability to link directly with object files using +the "C" calling convention.
+The functionality of this interface is limited and in particular the +dynamic rowset description and type conversions are not supported in this release. +On the other hand, the important feature of this interface is that it does not +require passing pointers to data managed by the user, because all data is handled +at the SOCI side. This should make it easier to integrate SOCI with languages that +have constrained ability to understand the C type system.
+Users of this interface need to explicitly #include <soci-simple.h>.
+typedef void * session_handle; +session_handle soci_create_session(char const * connectionString); +void soci_destroy_session(session_handle s); + +void soci_begin(session_handle s); +void soci_commit(session_handle s); +void soci_rollback(session_handle s); + +int soci_session_state(session_handle s); +char const * soci_session_error_message(session_handle s); ++ +
The functions above provide the session abstraction with the help of opaque handle.
+The soci_session_state function returns 1 if there was no error
+during the most recently executed function and 0 otherwise, in which
+case the soci_session_error_message can be used to obtain a human-readable
+error description.
Note that the only function that cannot report all errors this way is soci_create_session,
+which returns NULL if it was not possible to create an internal object
+representing the session. However, if the proxy object was created, but the connection
+could not be established for whatever reason, the error message can be obtained in
+the regular way.
+typedef void * statement_handle; +statement_handle soci_create_statement(session_handle s); +void soci_destroy_statement(statement_handle st); + +int soci_statement_state(statement_handle s); +char const * soci_statement_error_message(statement_handle s); ++ +
The functions above create and destroy the statement object. If the statement cannot
+be created by the soci_create_statement function, the error condition is set up in the related session object;
+for all other functions the error condition is set in the statement object itself.
+int soci_into_string (statement_handle st); +int soci_into_int (statement_handle st); +int soci_into_long_long(statement_handle st); +int soci_into_double (statement_handle st); +int soci_into_date (statement_handle st); + +int soci_into_string_v (statement_handle st); +int soci_into_int_v (statement_handle st); +int soci_into_long_long_v(statement_handle st); +int soci_into_double_v (statement_handle st); +int soci_into_date_v (statement_handle st); ++ +
These functions create new data items for storing query results (into elements).
+These elements can be later identified by their position, which is counted from 0. For convenience,
+these function return the position of the currently added element. In case of error,
+-1 is returned and the error condition is set in the statement object.
The _v versions create a vector into elements, which can be used
+to retrieve whole arrays of results.
+int soci_get_into_state(statement_handle st, int position); +int soci_get_into_state_v(statement_handle st, int position, int index); ++ +
This function returns 1 if the into element at the given position has non-null value and 0 otherwise.
+The _v version works with vector elements and expects an array index.
+int soci_into_get_size_v(statement_handle st); +void soci_into_resize_v (statement_handle st, int new_size); ++ +
The functions above allow to get and set the size of vector into element.
+ +Note: the soci_into_resize_v always sets all into vectors in the given statement
+to the same size, which guarantees that all vector into elements have equal size.
+char const * soci_get_into_string (statement_handle st, int position); +int soci_get_into_int (statement_handle st, int position); +long long soci_get_into_long_long(statement_handle st, int position); +double soci_get_into_double (statement_handle st, int position); +char const * soci_get_into_date (statement_handle st, int position); + +char const * soci_get_into_string_v (statement_handle st, int position, int index); +int soci_get_into_int_v (statement_handle st, int position, int index); +long long soci_get_into_long_long_v(statement_handle st, int position, int index); +double soci_get_into_double_v (statement_handle st, int position, int index); +char const * soci_get_into_date_v (statement_handle st, int position, int index); ++ +
The functions above allow to retrieve the current value of the given into element.
+ +Note: the date function returns the date value in the "YYYY MM DD HH mm ss" string format.
+void soci_use_string (statement_handle st, char const * name); +void soci_use_int (statement_handle st, char const * name); +void soci_use_long_long(statement_handle st, char const * name); +void soci_use_double (statement_handle st, char const * name); +void soci_use_date (statement_handle st, char const * name); + +void soci_use_string_v (statement_handle st, char const * name); +void soci_use_int_v (statement_handle st, char const * name); +void soci_use_long_long_v(statement_handle st, char const * name); +void soci_use_double_v (statement_handle st, char const * name); +void soci_use_date_v (statement_handle st, char const * name); ++ +
The functions above allow to create new data elements that will be used to provide +data to the query (use elements). The new elements can be later identified by given name, which +must be unique for the given statement.
+ ++void soci_set_use_state(statement_handle st, char const * name, int state); ++ +
The soci_set_use_state function allows to set the state of the given use element.
+If the state parameter is set to non-zero the use element is considered non-null
+(which is also the default state after creating the use element).
+int soci_use_get_size_v(statement_handle st); +void soci_use_resize_v (statement_handle st, int new_size); ++ +
These functions get and set the size of vector use elements (see comments for vector into elements above).
+ ++void soci_set_use_string (statement_handle st, char const * name, char const * val); +void soci_set_use_int (statement_handle st, char const * name, int val); +void soci_set_use_long_long(statement_handle st, char const * name, long long val); +void soci_set_use_double (statement_handle st, char const * name, double val); +void soci_set_use_date (statement_handle st, char const * name, char const * val); + +void soci_set_use_state_v (statement_handle st, char const * name, int index, int state); +void soci_set_use_string_v (statement_handle st, char const * name, int index, char const * val); +void soci_set_use_int_v (statement_handle st, char const * name, int index, int val); +void soci_set_use_long_long_v(statement_handle st, char const * name, int index, long long val); +void soci_set_use_double_v (statement_handle st, char const * name, int index, double val); +void soci_set_use_date_v (statement_handle st, char const * name, int index, char const * val); ++ +
The functions above set the value of the given use element, for both single and vector elements.
+ +Note: the expected format for the data values is "YYYY MM DD HH mm ss".
+int soci_get_use_state (statement_handle st, char const * name); +char const * soci_get_use_string (statement_handle st, char const * name); +int soci_get_use_int (statement_handle st, char const * name); +long long soci_get_use_long_long(statement_handle st, char const * name); +double soci_get_use_double (statement_handle st, char const * name); +char const * soci_get_use_date (statement_handle st, char const * name); ++ +
These functions allow to inspect the state and value of named use elements.
+Note: these functions are provide only for single use elements, not for vectors; +the rationale for this is that modifiable use elements are not supported for bulk operations.
+ ++void soci_prepare(statement_handle st, char const * query); +int soci_execute(statement_handle st, int withDataExchange); +int soci_fetch(statement_handle st); +int soci_got_data(statement_handle st); ++ +
The functions above provide the core execution functionality for the statement object +and their meaning is equivalent to the respective functions in the core C++ interface +described above.
+ +| + Previous (Beyond standard SQL) + | ++ Next (Backends reference) + | +
Copyright © 2013 Mateusz Loskot
+Copyright © 2012 Vadim Zeitlin
+Copyright © 2004-2006 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/statements.html b/doc/statements.html new file mode 100644 index 0000000000..81a3f3a8b9 --- /dev/null +++ b/doc/statements.html @@ -0,0 +1,389 @@ + + + + + +Consider the following examples:
+ +
+// 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);
+}
+
+
+Both examples will populate the table numbers with the
+values from 0 to 99.
The problem is that in both examples, not only the statement execution is +repeated 100 times, but also the statement parsing and preparation. +This means unnecessary overhead, even if some of the database servers +are likely to optimize the second case. In fact, more complicated queries are +likely to suffer in terms of lower performance, because finding the optimal +execution plan is quite expensive and here it would be needlessly repeated.
+ +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);
+}
+
+
+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 "prepare the statement and exchange the
+data for each value of variable i".
Portability note:
+The above syntax is supported for all backends, even if some database server +does not actually provide this functionality - in which case the library will internally +execute the query in a single phase, without really separating +the statement preparation from execution.
+For PostgreSQL servers older than 8.0 it is necessary to define the
+SOCI_POSTGRESQL_NOPREPARE macro while compiling the library
+to fall back to this one-phase behaviour. Simply, pass
+-DSOCI_POSTGRESQL_NOPREPARE=ON variable to CMake.
The rowset class provides an alternative means of executing queries and accessing results using STL-like iterator interface.
The rowset_iterator type is compatible with requirements defined for input iterator category and is available via iterator and const_iterator definitions in the rowset class.
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");
+
+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:
+// person table has 4 columns
+
+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;
+
+ // 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;
+}
+
+
+rowset_iterator can be used with standard algorithms as well:
+rowset<string> rs = (sql.prepare << "select firstname from person"); + +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.
When using some databases, further performance improvements may be possible by having the underlying database API group operations together to reduce network roundtrips. SOCI makes such bulk operations possible by supporting std::vector
+based types:
+// Example 3.
+const int BATCH_SIZE = 25;
+std::vector<int> ids;
+for (int i = 0; i != BATCH_SIZE; ++i)
+{
+ ids.push_back(i);
+}
+
+statement st = (sql.prepare <<
+ "insert into numbers(value) values(:val)",
+ use(ids));
+for (int i = 0; i != 4; ++i)
+{
+ st.execute(true);
+}
+
+
+(Of course, the size of the vector that will achieve optimum +performance will vary, depending on many environmental factors, such as +network speed.)
+ +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';
+}
+
+
+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.
Further fetch
+calls perform the actual data retrieval and cursor traversal. The
+end-of-cursor condition is indicated by the fetch
+function returning false.
The above code example should be treated as an idiomatic way +of reading many rows of data, one at a time.
+ +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); ++ +
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())
+{
+ std::vector<int>::iterator pos;
+ for(pos = valsOut.begin(); pos != valsOut.end(); ++pos)
+ {
+ 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.
+This explains why the resize(BATCH_SIZE) operation is
+needed - it is there to ensure that each time the fetch()
+is called, the vector is ready to accept the next bunch of values.
+Without this operation, the vector might
+be getting smaller with subsequent iterations of the loop, forcing more
+iterations to be performed (because all
+rows will be read anyway), than really needed.
Note the following details about the above examples:
+fetch(), the vector's size might
+be less than requested, but fetch()
+returning true means that there was at least one row retrieved.Taking these points under consideration, the above code example should +be treated as an idiomatic way of reading many rows by bunches of +requested size.
+ +Portability note:
+Actually, all supported backends guarantee that the requested
+number of rows will be read with each fetch and that the vector will
+never be down-sized, unless for the last fetch, when the end of rowset condition is met.
+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.
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");
+
+
+Portability note:
+The above way of calling stored procedures is provided for portability +of the code that might need it. It is of course still possible to call +procedures or functions using the syntax supported by the given +database server.
+The SOCI library provides the following members of the session
+class for transaction management:
void begin();void commit();void rollback();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);
+
+ ~transaction();
+
+ void commit();
+ void rollback();
+
+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);
+
+ sql << "insert into ...";
+ sql << "more sql queries ...";
+ // ...
+
+ 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.
+ +Portability note:
+Different database servers have different policies with regard to the +implicit transaction management. Some of them start the implicit +transaction with the first DML statement and keep it open until +explicitly commited or rolled back (or closing the whole session). +Others will treat each statement as if it was a separate, auto-commited +transaction. For better compatibility, it is recommended to use the +above functions for explicit transaction management.
+The following members of the session class support the basic logging functionality:
void set_log_stream(std::ostream * s);std::ostream * get_log_stream() const;std::string get_last_query() const;The first two functions allow to set the user-provided output stream object for logging. The NULL value, which is the default, means that there is no logging. An example use might be:
+session sql(oracle, "...");
+
+ofstream file("my_log.txt");
+sql.set_log_stream(&file);
+
+// ...
+
+
+Each statement logs its query string before the preparation step (whether explicit or implicit) and therefore logging is effective whether the query succeeds or not. Note that each prepared query is logged only once, independent on how many times it is executed.
+The get_last_query function allows to retrieve the last used query.
| + Previous (Exchanging data) + | ++ Next (Multithreading) + | +
Copyright © 2010 Mateusz Loskot
+Copyright © 2004-2011 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/structure.html b/doc/structure.html new file mode 100644 index 0000000000..533ce2f2ed --- /dev/null +++ b/doc/structure.html @@ -0,0 +1,81 @@ + + + + + +
+The picture above presents the structure of the library, together with its +clients and servers. The boxes filled with cyan represent components that +are part of the library distribution.
+ +The SOCI library is extensible in the following ways:
+The core part of the library and the backend interface definition are
+placed in the core directory of the library distribution.
+The soci-backend.h file is an internal abstract
+interface to the actual backends, which are needed to perform
+operations on the given database server. Normally, the C++ client
+program needs to interface with the soci.h header and the
+header(s) relevant to the given backend(s) (for example, soci-oracle.h),
+although with dynamic backend loading this can be avoided.
+It is possible for the same program to use many backends at the same
+time.
Everything in SOCI is
+declared in the namespace soci.
+All code examples presented in this documentation assume that your code
+begins with something
+like:
+#include "soci.h" +// other includes if necessary + +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 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.
| + Previous (Contents) + | ++ Next (Installation) + | +
Copyright © 2010 Mateusz Loskot
+Copyright © 2004-2011 Maciej Sobczak, Stephen Hutton
+ + diff --git a/doc/structure.odg b/doc/structure.odg new file mode 100644 index 0000000000000000000000000000000000000000..64aa0761c5b71a18ad12e8164348c80433522078 GIT binary patch literal 29603 zcmbrl1C%Av^CnofZQHhO+g6utcU5)SMi;u+WxJ}&wr$(q*FFE4ot^WWIeT_*oXotD z_eR7QU*yY^H{W|ovY=pSK!4kk^Eg)?C__Ijp#OJ+0&I5HcIK{Lj^@UWj<(jO#;(>5 z_KfcKW(*F-F4is#4vyybW)7xqcINi343^Huj#k#DE=vCohX5b{qu~I0q7L>J)|PJ0 z|6IASFuJ%JySllU7&|lmH#;am*uOyjhY_Gd`5)|zO-;>h%>iy4oEc5soSpw7@U*i9 z1yYiQ{0kfiQu$g^yR+Bva0had>MRePDCxT5?ptd%< zaH(ly*R0G${o_0nv|rRpKxa1zdjNG_Wsv?|aSe6c+Ig9>v|?Y^6J}ZWS$_|8e5YC5 zX7N$Eo{s;G`ZK$zX-eHbRoh!0$N|bQm*A7Z;s<4^6V)E^+~WR7SSo zO`*EfewWAufrXxbgy;`nKXwm4n)T4-U@Nj;YGqN-8B>sEkwoxXeVBFKfR*DHmO*di zYHSC*c?m|J#{i{xU^TWkDdT8|a#`dcOG{BFjAH3b8~C @V+9b3IwgqP#9 xL^M&*BYw%KZ*fwnYLMi+2B;+&sF%a3+z&I8crYu~Fef&7 z%>>is+lITnmy})uGd{oS2<(}%o;O;Y85dNsk3_LGN9)0FU^AO)q7Kmmz2hEBnE1B5 z8HTyK)D0xbgax9y9<3atr5>2E?{te(M3EY=8GaV$p)w3`uU-my^+|52QM~>^!zmiJ zmDEIGVI`L4{o1HnnVx4u2egauN3*F&jtwljRU?hLO^2y-e3WR!Fjv1?j|N^zMK$Uz zyUBr35@V5rF
- TIDRVVfi5s_x`UOscV7`xC4$rLR-BT!0SK_v`AjKSOa4qkR@jIRD= z0^bv~Sx(wye47ek!td>TduNP7=ERwrm|rD>9uPjtMv$~Vm9uMWg0;SW?7u^zfSDX^ zzY@|73662~e9b!rJfnSCf`J4sr)#GJf0dpAeHknRe^%CkBHo5Bspe*IJG9q#=`_}L zn#;;2G4~ohe#v|-7>YptMC54-?FmWKxKgMJChVuG1%c$mya?6MFd!%j6cI`LG|8uT za4)ioRi`kVz;LD2voN|nEtR#Xxqocntz$X#YdYl`^>~_alE+G8GvuOCv#OCxKDho9 zbUqisoqdsRF;`c8M%T)_E`gifxp;Y>-jrlb7{$On;D=zW5w$VVoYy6tK@7GfW^GWV zi+N*1t7^OWXMah=*_U_yz{Pj2BLQ+Mw6#Oi#Y0AsH8L+AVhe;JDWphV;?N^)=;Od` z7*!N&u0oat@%EkHV|6=bi{plbF7R^1)IV)76TOXHL$A@Fw)ggeDJ1sF=hXJsnmi)R z1fG|`F{-j-e}!tPr(}<-TXJ?itoi<6us+qcx+7b zz(j-UB9Yv2=YjUIEsK`DeiVq;%uYs4vt5yxsgMasjqj3(<8moRCd^|_+bz;ZHO@04 zo`zG5`ItrvL*E_4okY9SKW{LY2#%e1lD1~HAOlee+?)`7m!k5Q*oLKB7c8zPN|{;E zgIV7?a!XzFhAH8aO`%W{AtE3&AVQzWb3oR$F@7#w+_9anvgCSQiZs>l+dVP%V2DaI zYj6m^ am4>h;;fR2ygtmq{@8BIqWo1#s%fq{T#zL#PydmPerBeT)7xS;45ew zB^vuM=-8p=hq}1_Y%u>?!8(4D#^pjJNEOOstloA?8zR0TVjsZ+hV+|iACNS|WT^Jn zzJl%(%Tsgg)!ex-Vi}Q9RWX!+$PtYS5)STjLLNe1#8wZpPa4q}9Lk>A{Ip+Jvi#>I zwTE#zcWjp}ibe}h&b=d1e2b=D_PgXYICYO>_kXmMBWVb{af8SQ^%RG|n!+ax+Z`H* zyzh?0VJuXZ@hrlBYyPQem?)taM0uR_L+MSwG;Xy`_3Im);R6;$8?$kg08Vxr@yQxo zi7C0O^r0Y353~O@BOgS+{k>;koj*E$L%f;^4BQ!9Nc1$h05mYO$K)wTg>w=Ekt1J` z>_Ju7rdHV55}8+ajK?ES)9?-?3VraU`7;R~tv$ZDQ=+kWR-^Te4a I~rxglMU0SIKaU9dKm$gg5=jNu~2CcnQ0G^Z0 EJj@~a{rWV{vF-C`Arm`^B#Jn6I#~QhWc+us{oqpB;8XGwQ8pfiko<%cI1X7e ztWy8w-q1=8vglt-7cS*MV|uGAVQTP$2|M$As6qZx5Az_4PMge=%#)Z+yl6``$v!id z<&UM!@i$W~XbD<=*F+y8?Tq$qGVx=G&+ENBN>r7I{9I+jEPAn0l!4)O5gIq@QhDQ6 zsHuEhygXIIVau rAc=FN5hKon8$N_mp*JPyw?a{pUQFj&P zIRBmkyI1ga0RG9|02y3PH4Tyc@a;rzEFwTUv _LQ~H>r!!dkTuK@4M#^t4dDY8M)&RzYP7Cee>al3#P;nj_yj44hDFADWe ;YN|-dkC^?8Lo*p9V zKZ% -z#bss!+4liM|EGEFC zOrxmf*@>g*x<@h;1MCVx@EM(3t5Qc~Lc+w_6H*9fBOJ%3Jtp;-N)rD0DcYa!Y`dEA z jm;2JbqUM)1ycSm>hs61^&uXD#iFopZN`P3QL*!ynuRv;`*JTd4zC#rl`Q?R zKoa*Itf%(xquNdhvxF))b_&{()~VrveYcR-nAV-U84GNk+}769Y3BDVYh6~v56Emc zkb+4-8gXhN<|MCi%=Ex}S{(d_kn-V$rV;aOuI@YeGJJAxj3(@Dv*&_HD9#bog|(5M zW4@%lJsFqXTW3p_oMXq^HY2Q0;}L8b=uYMEzuDC_&Sd-Qqgri?zGIn2Y;)Uf22_RZ zZ41#GJl4^{GncsFcdF_zQek;@Uezhuv~&fnQqoXsQxYh?;P8z_;*r_I(*7cG*<^ia zuQrQ!L1#Ghrm@rO^ZK2ruRl4vd%Z%?KQf}Ntp`k6YmS=hp$s!(G`B5uh_K0aPZIUQ zJiSF9t&>CV;5j0r<}Mr4pRs6pS)UqCJ){&Q&&VrGM!5O3q>T+Bq#JcEb;DDEbDS~? zCRSubc?-_R<3~xRfGuHoIiTnDBh+pm34K%%lUD1 <`K!T?b_O~W?w?IkBNqPGiX}%*A#lj_ zzJ~S1-#KVn9LC7W1q5-+I;w0@6%!dZh&hi8d9{x8aOTan)ks;{5CfaYA#tO2j6oVE z;bjboHR{L8oN(eJV?6T_CJHtj&uUe^@gbPcSS2HJR&E>ULY90>`x$@XZw1i><-i@C zoyQ?VEaWwg&^>|1+CsTOLlNJwJ{*4sN})Zd`0!?Ic$bIDfiSDu*9U}v((N5(ZcZu4 z_&UKB<#M1BA{k}sAL>iqX!%`}J; AROxDNmrYqK&@VqIkyd;TpqeLG9hnVQm_4)()O0 z-R~hLYTRgaoG}j});-seecJC6U$lXDsdVvwy6M>z+J=xJH<3g1ar9f`zc>x_$YW_W z `8KszbKHS1 z-RdkQ#>|rn2`zNM4+RqQ4j5RqBl&|6;cl>;aCc8Iy*CQ`V-^gPyIC@y_YT%wddRl>8u|2}?Z3OF#6Szxfn094Qs%TNz^; z&z_u}>o`dtMj24{{`G0zua;h3nl3XXn9|~Mhvq9(sm`UiQ1>ma)N|B+%-O;_%H!-u z8+nJm8xXYi*SA)QfnSS1qKV~!Ph`sge;Q!+^3V?O{Xy*Dge@ aVbi=cfv?BM{ErG75d50bd_aC+N2x0c|aDtOlp*f+ukoWRQhYanHb;AoT)i)+q zi{FETv9AG5K2~;%vH^%u#D@q_)_z5wgC14yIyBquK5kT}?T#B+-~rpMb5AoDyU|Xf z&R~`gJ!_ZtwC8*JlDmEr!d>%7d0S|H=wzBE2Leg{nJkenteqpXS7naGLS!STo@m36 zNI$?y%Wj~+$QNYa7N+5IS}6G?b-Qw2PXpbWdi5jNEyT-b@bW|GM!oOu7Z6quHA;kA zISlQ(2Jzovx(uLFS$Z9m`{bmS*?F_hq-5{vaDjlpfqbL(iMq!#OnP z;$$`&aNA~C-Wku~YO#KWPz~wcZ932>e)@rKXgs?+NU9_SCuq-;VpQazjqpAD%C+n* zXriq)@m$F4r*pt!-RqGDHgkF%(fP>st7HA %)7dT zC~{KT*+K;~tOlYUMAuMT)l9$-GWL|@21w?MXgKDUHmXPZ=FQD$&j8a0MHPv>Bw85y za}I9H+@c2XVFQ?`{-pKXN;jg?2c9^}K3&zJTS2u 82T>ZRb|4vB&`Pvo z(}i1k6Z>FHO-d0$S$52s&;;So_+x*{8G(XqZ>W ;@aS67ll+TO~D3_pHp*vz;D>b9FeeAc18%p7rt*SF{ ziuwi@|6i581CV6R+P2&7p6Tvs+qP}nwr$&*wrx$@wlQtnw(UOs?i1fX?|%Qizc}Zq zh>EPJ_1vp+<*KZVtjzneA(X9!1Rth^I;$sIM Ou*#N{(5eDtHc|u;WA6Mdi`q+0lm^YSE1A@u7t-{C?1H z oo95~nvVSs$*c+sZ$+QYy)h63k$VozqOPn$o_t0@8mJjl9 zM>>5IgFc~o#m?vs3Lrjjph`bg1h=Jr;eV^U`m!!kiaN!YoOm UL+nurK@m%Eggo^TRQO1Vd%74IF3MsU&)2ot5-8P~ z3t^H?>2GcumPrv__Ab9NOuxs@wzw!M;LR83rW@s0U#}@3F38*gozqoiXOv(A+^~A$ zLX2#X=nC`9a 6Ai*Wql;$tC0Qi80&Af}gYO7NsM3Zi6k^4I9kWhEz5O>)LL8Y8o z0+q@Lh%ZHuk`U|p?P!e4o)hm;_vb^DPR+DD{VY`kf60^Ll=5A$w7bk8< u{`8{Db{T`(vh6YAxi?U 8^=3*FDY4uPZjb!sJ $q>d>{PUEu+(xr%0wGmygpH#Vm{__;#RAJqh4yAp}Vg)ia6O%eT-X45zU?d z$L6f@=HfW5#*f!{?u;>7z_WN%zwc$rqa~zv*@3T=mmc z8{?5HxmW4csFY4=3b}a%iueW-d}>(Knn5P2+1$11anSk>hDd#s#0f{{(VSUTr%l@y zri# ou2v49*RA23v#DeI+7X| zDf4wGW`BH?NB?+;u zJ=vdl&_^kWF`+ Cf4(Pbv}~@8OmTle8Wt8ovc |0lFtC^cNwwshZfVU^9X z-6?}ZdY|=l6Le!qr%^J03AP%aUL2Y@-z15fi@u1pfOW&BSd>n$Z_(ac>oKMU=dHSl z1cyfT$u>!s^se=;v_i>ua<$heEsSVYIGaj*0%?5ZkQIZBM~KJHr;odp7MylFoWO<5 zwKm7S6M}j}MFv^xh|@A{V$OGZR+lq%f=2`yE(F+ke+47QARQKWtuis2P$R5+1-&8s z0ohQWsk>Pf5-ukuRS*7k9iw70Z;N3qq}i7|3X!0f0~AInk*5{hy*`x`3u4f~JmDoj zTu+GHYs-#mY2g`8E-BQZXjxNHkf|D77w++i1C_qLA^CK*?s6J`2t^94WSBW~Bkj~A zlW1&@l1(eW86Cooi*02rDq^43N;|Im1_1n*VqeNlj!Vl*iWH;5`#Md5NC9Zz2;iF+ z#HKZVH;?E;(H*(;jO*235<^up%~t;8jtgO)1)4^#XrYBaqUt0%_HBbuG8<_dIftNg z#aS35gizU%UmULYyIKxFE(90}0(HY<5zLiS6-!s#OcN%9gt0*C@`U5e5Hyb*!kk*X zG9@11PWL9^06k~k*PImnJ7|CStZ6Rl^q)@lI}c>$IouWW1GU$+cT=L1C6DX_IRo*B zm4q!ojRi_?7{fATbmqibn830MFIQeSGlbu^j@OltZE-4M*sFK$P4-muVH7JmjY|l@ zj<=P`Icawtc=)guwueP;4Mwh*mmhieBPrjbu2L6kR0qpq*Na;ujJA%f55&{ATU<*R zAa647jlCk;hqUsutGPcIIEo+`&{a*Z1E-GXU?h#}PLaetg)s=g^95p*ztI5AuB~(C z9UG|?V0Co9*pRkTb{)Rjp5EE6cT1M!{Paon^67?-I}R_H)SuNEl4*zu%}cA$_&6XK z5c_~8!RBvG6Oe=vAJ4PMaz*w5(*}E~F;yF&h$`n~N+G23zgCS=^yn-t6aplscQrOP z+Y29C-C4`9QMxz|FB&*!-X1RC4epWVA4;7V_w;k^OIsyQ5p&mp7mhBoUFy4+H!6ES zzL#ST7cUBix-*A 4SZ9UO(&g7~cE^&OM zsIW{qz?$PGq_40IZN)GGVvopFu`H!hB*Yxv9u5+A4@6CBxIbP>!#=g7<2D>cEm&`s z^QRco*XGqf1r)%AJ>wsejHq_EnVYl@`Y~qO2|+FgCKejMMy+|y`b{iqnu#CdHqk#t zB5Ej%a>siUBsgiQ`AK6RT#+T|;1~7|DI)_?6&M0y *VJdCW ^zvcu*5(sTEH 8}Xy@5L4}o>~1p^1(WMPY=)wcD`hrKW85| z5YW563%FLnC;{Ocx@QBW<7RP|*kRqTyc08#TrYL&`Hw1&n)lk~IN^T7Kbn2^euTfO zNFGF0n|zIRF&Ebvc6Q=%0gqv7x-&hnXrL|Et6M;@KUuDtXGfh!d8yo3tWGKHq=eJ; z;eD~*BJi&o-g$C7WYwDN`4;B= vTchhWTs1C!#&_WmkiGH(0G&7QVqF=d z-?WeDJPA!@2eg+ZXVNrZdbPkve*=1i-nx8GDEi*Y{D!Ki92gE>kjL5U(b@qoP8)!V zenisTf4UYFPocuBwir(h;#i-ZoVBNHlOQ81s{=!kP0ys^JPD^Y+R}vyy9;G}H9uQV zu=}*IWtr&;@2JQ (R9ChO0Lg$k&c))0j=UIzS)L+gs zL#k_w66a_oh}$oN`n4b~B4RGKtTtyZNw(9oHxE{en5nwz)jKsMxq_npjDf(#705&X zl8vnubuh{2c^$yM<1WJm!&w#l>^5T=J|6cb0&2OQ3YF6rnLnIS3`v&w$lJ2_n_2k7 zH(`^iQQ(&EIQ%5N; (|KGq|{ =`)popNL*V}n>EG7GCz)6ALBM;fcGGmOpbZ~(A~%vvGF@d-7O#})Db>@5QZ4u zJS;BAaYm?}4Y4)ceanP3rr|a|JUpO^q|XfG>`YrDP;5(5zpYhLAL?yCa*BHlh+H@} z+6Dtjr4O;qUa#Z=E8%8&%l!BEDE$xH1Dso_y^-y(YOvKk5A)+!8X=a{I5?UWZ&9qF zR1h{H9?!nvwfblwA;Lr{PGI88$IIl2*#-(^y|MWOW;Yvn&g}7gZb8l>M+Z)pEfdpz z2e8w=XuJdDXK+L*&mi@JfZd>Wp}Yqi_(kuhnz zxqFpD4^@yLJts(szYyiXNIpP5hJQ`E ivN4sFP)W-oxSU0$lQbeT3wdJbXRwt^QE^9p6yC5x&tA7No7h6mVxeXEY@G{5Be( zF(x|%eSZ9dw%O>l^^80#Hoo8zly7$AN-Ny*$6H45o5B1~-vd5aA5glgQbR{sE+%(B zl0`z9>o^?10q7?cY(9gVpY+Qbr5)DkXQSU+*Y@I6zF)w~j92PM!93zL>WS&Dg$i1B zeM8s$T0B;kr)?eUM$QSd3KQYxV!o;O^Dfr&NqX%%y+i`d)Wb+Fd)$Y}Fqzzp{H(CC z3gsmcw!p96`lzg&sc4jTV@De0+D2VCc9d71#_~#>KXwB)?MLCJSU)mX!8GlwKRFCU zw6sH>v)4y8Qj+w=0%WT{A7~cq9jBO?_ru5zKiv%iI@C)+{;C$S6-hN)_J!tgLWTGp zOr3*|={%y*STe( CL6Z`uag696?!LQhTWS1?F$KZbhr^Ft3Rj)vIM^g#bI2 zm1ZyzBF?*z3W`}n2RDytVltz5J2%KX;2YcIG6yi0{s&e=P6sW}^C`AU$3^95!iHC; zN!lv!G5dv@{1@#)i=jeqegB2D6 ze*4{N^oB(hSNJp*L~^nxDy?hALD?`Us%o?CXQjhw4NIb}P30imhwv9PLKXKb0$q^a zLZU6knf84iB|VH7z2)IKD<_Qy4?n;yW$ Ib;#4Y|RLDr*pKnBuX-obL20xT&Jd2n&@S6ZIpa^M`2<7A3qvmdz{~jk$ z)X#}%2v^Qh#WxXbmqsA2fs@YY%_4SfYgbE;8`feMiyjVws}p-wFofoG^mh8rm<9Px zX5)39CN?Lc8}nnRN)q>WI_H!pw3qXiyKKXm$ZGS&Ln?A4b3+pVzaMvp(vO{uG#xN{ zGA;wG`>f*KuOK*+OU0W<>c2Ss`vb}%vg3UZY=*CE?iFK{cv eR?LMNIAzP>yjL!}ZMIClHaz&Y)npp6Zpe()>DB4M=9tv9iNw1c59Y57bkenq z0NO0q9n6?7d1}kqcYt2cbBwWigPlxxOJG@O7&oYOu4@+zZatw1v0h*fIeED|9yoEg z3hu~sK^)o%ZGrb&BL9|PI@6DWs0TXqn~9aV)MIqY9uhGb)ne*y{+2mKaj2XFAz`6* zS%6t{-W$c@=E?EaTsG+ M{J zEHy@1HvBtKMEslw0jLD+Lo?y&^&3z@G>dz)j-F<;!d#UGR6fYYp;lg?7VK-TymHle zThym!ZO*=cpcP5IG$=C}PM*lAx8i|l(;%4*gGuzp%vj^Cv! @-L m&$!hzljaI1@AI>BJn1552 zJLHD9pTC&bW8vwbd5*39eHRVPE?#BEVI=!G#d+=PFQ)@ygbh;0q~|hTkZiy!p%hVZ zp3)$*0K*&D)B3JcM}m1Mw7Z!nhm-kGJG{>+o86JiSVVb!$*h33=L8j^ir^0q{Hxhy zuX?$_1-q6$)iDkU)ZHV6!uyIo&$mULq}gCL4*;Wxxl!thNwIBs#MNcWA=WS|gb~>S zMPsHg#3M*gChNBK-&rP}*uP5>uFaxvw=Ql-G=%G@@9yYFp{+zb+?Xh&BTRD=pz)QS z8X$pZV1pdh*+F-fBQ)vDpiDjAsILsxQ)D3Sgg|vss`KcFaB7YC>oOUqg7mpt+JWsg zharP>VSath<+IV_y2}M&D)aAwlw$WQCOVRNBuH*+jjo5xTwgxgGo>}iSBQtlVt%Xb zD}8|jjnkGTQ8gh;-JzT5=FGs22mWa>3fwEwBLZSGh}6^KN#XkHIu?XOj}zB-%V+CU zYM( UO^`>U zbY8%Z8)FIZUBJtJQBB6tYk;Wd5x%~RLOvOJjZeSU^pT4_J3Wq*U%SaLh3-wqQ0ane zh#R-k1MlYB!gCKN^u6<`Il(gySEByU_gZsKb*s3-(wr&s*5|L4@WcZ*U`ZdZ-iRb4 z{o;c)uGJHIXs2xh_LIkgB*i7{SEiYVWfq2&{0&dyzf9MA!hp%_9>TKA&@dLME0p5i zqV1J8rZ~tZ-E}!%#Rv6)?YE _yX9A5Ph #45e?vfy>PKh3u;%MicGA<#)EX~ZBoDF zJ20FAnBY!eQh{xWGlm$3?82K#QKoJR}Epvp@Yk1fMqqP8bpl!x*Owb5K!SElq*|*x&$Mg&IDA z7%JM-uRuJqxP#a?$~uuX8Yo}&8Z4nA(l99`ExoGDS9>xx%y8aA#Oc(Ff?xl%wu=V) z!SVX`mb5l7nXvBtAkJH1E0HQF{$Y7go(xR16!=X06KQ|N&O(q={5LsIv;)V8V0;>` zFh0EsH07a_D;mBOC2y3|vG4?4iWV!;Vfj@t?2!AI{setoQoEhRValCBj8~*fq}#bk zt94DK(`}4Mn3MJ^@4EP@__$;px(gbPRdB3OU)hMkqk&i3-Kph^6Q6s+k_*KF)F+A6 zJcM}u9=%&(pD4LiXf*nL8Zg+Ih;&*i%6M!SkGr_=CQ)HCl^B%ojUXS6$#N Wt-6?to6k`o#J0exOT8-_mTJ=ZM2)W_lXm8nIEtz}}j7#&Jw4a^z0II~J{M^9ke1 zkP@3NIXL*a_&GJgRAOei^!O&$_gP>%uPI6p{dp{jhJvx!!d&1Lrztjeu~j-bYDZvP zonS(GbpjS!4tn7dgJo>ft_tz=RnQ!;vm$y`(RpMAUXGC1<(6FSMYQK(PcFY=TroV* zC=HByw$m2PrJ$)F=?20_?Xvxs!mbsCWo1PsjVY`UXc@JvDU9ha0sg{KC>T)gGw~6v zd93;| }ybte}rJakU{EytT0!T+ZwCSoq6_i`4^?jJTeA zp&k+6ic#*zWA+r1tKzMNT1;%14BU7cnG)czH5pyJfLzLc4u $w+>~FVuRxK14G)6=fw0H @20#a%+s~+=&3Hf`NyqDpvHsIM-jJ$jNf66DRaXyl2y4 zg>1$Wm>gt@>xSFvOtyA9z}1Ee$-n_wa|^6hy4TPivAwQPh*-excdfTfa7x+^n9KZ2 zPbET|-nC~!m=LIwGjI2X(VBai!>)v6WvR*tc4Y#Kvg khpRW$omO`F*F z5dRs(Rq8}baS}t^7h-HYYl#@s7bFniByepJC^%A*J oOAA!|xjF;?xKUTW>+IP*Ix zepc~%AU&r!Xb@7YJUL2D5Ii$pKUy2=?_6iZU>Uh;suWNY63nT%fr?Ld@XrOA- 1mKO(iC0KzJHHnYjYzJ%j&k6E4x_n&S2N#jD=&`9(ZS$xGIT1mIb!r@UQ^>T5P%R zNE<48-TJ%_2F;(dKyO)W%}1}Fk#+^AZ~2|3yhS>Yq7GqnvZ`T& Qx36 zF+-SFW?kaaN<-i^gkcE(adwd5l3|{SirV50pPDm0YpIna(Qa&h(K$m&3GYWwmdY8^ zg=25q4MqaR&s+Gt7bHWKmu}a9Q_{IfmLWVDv5Ug!&_QPdjdN5J!Si%xp={(wbU)S} zQ}BFZE%&So20U7E1`vBkWG >Xk_(=P+lb!k!*#6$Yb07F3jki{>%fc9$LDXk0C%hZ{v`SKPzaDfKrruT& z`8scYS1E=)@n!RADvDpYkz3{Ud+}?txIfKOJSeGL*6o5J(t~QwfCN~59hML7H8gj; zj9ERuCg0?T3pp(OCGEtHR+}Ewa3DVJiKVbmLYjEV6A4cgb6?9dsT{bI@@nJaH1{S; zF=_~HVotq(0@zBcwMr08oVR!;XT{g)3gYST9P>X-(9#F$bI614Lp!BL-X-9Wky!@y z%1V*~rGc*BMLjn1U2w`u{>s($D$6Y^eJSH%LsYl~WI1E!?Rt}u1z2+i5z1Y7zVXGk z*HFR(hv~^KlZ7OD(*y69gXrChl&e}VmW&S3jY^TTF$blVG?w0}E+rs}z{|#?1adx^ zresc56v29hki^juHkTN~)tD6}pJ}EHHIgcN6$d_vq+x7L@u~tjU1VRHD|CKuGnY(g zcrLR@V+=>5ybg;wk`pl#Z)vG04 >&+yUs&^35&mFvL?~{T2((bKQ&NEx!7n^6 z%!8EJX_X=0^e*jKiW}NU%%Xt2ZN~p#>#-}Ct%?cRo8#m*xdzrFT#iBSNcwEbEXzG; z%5_pr#A9*;F7kF9=g`uj6-uCN69lU`y3d4rxke!C0h6#S&a+>c@in&fGrN7sJd;;x z(D>xLf~k37hK(NwOPkyF^tJ&W3NBfGQi_7Lye69lOHk##&W%%K3D?1gZE;T@@sFvn zv6v>!RP<)MQj8Sc*|mvT)B<$-^b*W?-< mc}29`uXF)S-SBDKu%14 zZcQYDZ+Iv)0OCSZJS{T|lLL?~Z5@EG&(g;fY6{<&PbLJ56UdvB@S9eDkxZjC02P?A z@d1w^F+fOY=(lcA{ppW%$|9Th%G3C%?9=?1fR@h0a(V#L<6kyL)})38=0 O6(GCEt`BuzeSDT!)iynnT#$TNKXf!d-?w#*KWx-2&j6&8c#1CBHukIQ9NwHv zULaq5BwiR!s|N3Ao}#fn13zc(f?vR22tKnJmX<#CUUlZW9N+yuE h-~rRUpU&IU!4iOPMIVPjeCqQ8_ +iRPSL@GMt$&e zFl;B=Hx8i4CHUqRIA{~vb8!pe2|;kchavd>Q$LEJ*Js)HiiY3|!!Y1s=r?m99Hs8t z)%*b!WR>~(w6m!)_J+eAc)yv=s4?5+^xk0#vSq?yfPp7K;-G<1(PacU&%_AGJfW=Y z<0ARF8cz8cNA}iP&`ttWl~SC7ihw{tfq($cfPnD&$}ri}^QpobkpUg~DTd-z)Imb@ ziu2S_Q}>x~*TIt1Wp+86s=C2pJk9gyg631=$|5DWIh-OK_?p@OT r5QwBJ&|s1NasX(|@1Mip)&B z@Y&sQ`o8P?8ELTlhEV$1UE}-VaM8x8{O~#0VWa!W_Sp5zYpI#>SrG6(DgBhqn#9BR zDTBG#HSAzN<+DkS_0cagyw>-$uEZ;(vHIAr_rZZhXeDV0=p*zz$qSMLx@Cj_cK{1k z^Sv&BRdp#)HE_d)N%;qz9vv}VOmzxGJSk7_XEx}S3Njn}F$e2A62X**>=G;~nGjDn z-W$$eJNEU~ioq;-004CP|F<3cP=7fv8#y|fS)2UJgLz$j(_w`b*1M| I+POdo^FPC41ss?enqcsgHC2apR?6 KGaBbeQAgiNA1Ebe14cm+um0RnRXFBgb?Y*7#u7kNFJOLQjLcAX}%VE7?gWGG9 zzRK;c+ZjDsbZ@uIzRW(F6t(p(;v+87LT|zOOOdN0u9C0kYX;M}z@Z|)>k5eDwb+9c zRjlXfIm6 j5uIWsP;hP5ydTt zw}y+YPfmG~vk9KUPbT6d#4Ym)sPoX=YMO31G>2e#*!+_aZZQN5Bv1s|^f~QT8D9dX zY*da_9tOQeFN4CLKr%KEk6`p}3GBrGj!{c*ilbyvs$uz{RxfW18@OIk%?1JIo2$>7 z9RF3pth0-EX2X~vS}6e8^rui7;4KcWs}|y;Q?bT@7}9$YGnDHnHJaWkzOu_hTOdGF zinU|$$FJHe __`wyXPnR;(AAxs+ZpGS7Q;{&O>tVTDdz7Lm*xGo)iRJp1)~RyEENok%vow z^03o+(mU+gmr(GXnzJIkoLa#Hip1H4V`}A4Qk{pXkuwI%U~YZ@b9F9H=hMvCtIul6 zX+bABhbzg4@Hfde_?S?>NJ5%NYZ+2EH*-6Zwq=gfg`q&iw84}sqrhh!oHt2UwzCqC zh5~E|!Jl_k9_f}KVp-^ G_Opo?w3wWOsRkIHnHkR*%;f4eu*FFQU7cjTPaJM;6+e7<7m1Am5LW}kD4LLpgC z6jP+GXE>0Y_E1GnLgRqfERs`nP(`yZ!bE?62m*9*q8d!`U3Wk?ptw1OC H&}v zQsko&lNP42(z7-*HgfnEwX7jY&ZZX^)_3!f0)0bcuER2N-jJC4vgaM(*P=MTpKb88 z5081Cv&NTy>0Lq0)FsEoQPBR&-0>L%b|Mo}ItoLQUu>gtVa3vK>-4Vb`xCR}Z~5rF z Sf6{=T-^*~5T!j8T~!!zu(4T-tH0{e^iMmFkPMlQDbjC*#uJSHw^bK-lB z&a!md8Orjq5%D-uTAG>zvU2s>%>i*k7lFj4nH+q >CNjK` |7!;iOY@NB({8a~*fw>A8I?R4_0o&RECr(gwYFld`*>`Ge z{#367oagG)Ja0AZvxcdh{a0h9TJa^Af4Pr3e!1GpN&o^O0w8`h%zx(J{p*eY^YlM7 zynkl>p98)MfAQh|gxpv0pNzS`Bk%s-v;UoY@ITzTzo2)2BJ`{HPo4jQ!291m++R+w ze`4#a_)pP40K9*{Fn_ZCSw-ZpOZiWgzXS39yUt%oygwoRpLG5WkoVt}{=Hry|3T>w zFz^4XO6Px2`2*1V? +eH6G i0%>oB;lNA#Cx^N00cul^tEFHkc8 literal 0 HcmV?d00001 diff --git a/doc/structure.png b/doc/structure.png new file mode 100644 index 0000000000000000000000000000000000000000..1694cb68715464bd7e21004cb38f1e3dca9f47af GIT binary patch literal 26919 zcmZsBV|-=LwrzJh9jB9YY}-!9ww>(Qwr$(CjUB6F+qP{dFaLASyZ7DqyC2s2uu-+D zYOFcN9CL=sN{hfkVL*NR_6=4{R8aogx9>Y&R|*8k*WHL&xc}=5NE=Z#`)}WVjelKZ z-`(ElSHFETSr!xIQ*_BV>wwijTI~4zT+R;n8;;`@L=^K2Qy-$12sFPU!RKRYv3W%_ zVQ%m{2rg=6@`g?;kd0hIp+-pu-7adWFzv?tyD$LEVeMaqsF+SS1Bj}w9Nm+r1ToEn zDm20h4w--08yPtp ^34=$aiYmK zI22&R$V6CBD5Hp+Q S-99W>~)l+JJL5Bow9cg0f gmv6 zb|d>$9P@6*RgN8SjWPz`q_$dfWP+ Ub|t>QgQ^goAYm01}-|8&>qy*6nc%;>pkjqpN-m7O-)Vn6IVHCLEG)ntdTl( z!n`gbLRYgH^hu6Z?2RRbJx3%1YH J#Jvf zkt_Be?l8mL6;L|M!=;;eIL>UeM>5ztceCM= QLoTza>rkk&c#^|fH_@g zs4n~OB8ne4_xEULeSw?xUarOsdxSkaDG`pMA>GQz3k`bj40HGRrXJN8yR^<#8==OM zAkCl$qT+|Rbnp^rX#oL5-SzPIb$^q<6$ 9y1 zzabj<-9mXt$Z|PXWh#z)zz<#Qr*T(E%UHQZn2&;+``?vo0Fc}}_(CW;iWvuUsB%;+f2Dr!I>{eB*sT|4ca^iI}m|&0o61(bKY{@~2mP@0*x&w-!w@o3w zvR&2(j}~-}MKg$rDStvrvie!~^-^OOUE|~G!#grg+OYC_f#pYerUuc`s4sybUQG}e z^tEO=sjn=c2pFd9c|8xWR5x|a-=2*GDv$t7TsiKknMw6Oe5mD3aIUarLc5JRYA&HV ziYD`!6%MFhsLVI!DqNwl{iUQiADDCNAV}|3+tCbV^kq0Gr1O_-(@xpgczjeoPUjfS zamp&!T=E~E2wmIOx%xxQjRzIC$yGOkZp4U5>gZrRLWLgZj`qQN?FQ-cqi$O>W&^Sb z38iQ-xcGR!;IRQ}MG0KRT|vxHW{N5v20dtNjX4m7d?|H__=T*Vh6l=$E}9|U*EM7U zSn|lNq+mfwEAd01vz2wOt5v8~VE#zPXhK=Bb%OsSAf(x?$L&B4awB!yrI6-igL4ii z70#K!t4rnH%jI0-rWd3BM1z8>)U{Vp(;Qovtr4KUyJJ~$mc_dttAZt8v; zOZ|H#rxO-zZQaM4LByup&Q3f-U%h2&GGskq@2`qobdqMWuT)l;@ieTCCsJr;XU-vA z=knpxP`S$=oH=LU0)Haw&V!$3YJ-OA7520Bss<~{{5R&y%l#<=0>(%C(p1l6s5El@ zxLbSwt-u^%Gqu+Pb8WzEKv};EopdA3bd@3L#*}m&j`sB%d^I;F_AOMEalUmuTvpIP zip2EbDvDP8EN?shn{o>|JZ{r{U)NORXMV@zB}kn4<#w>8CI19BQvyFB#6`c+*ksT( zPt8O64Bb^B;g~V?l_aAZO5PqM+Um6Yk6mn&F(chKN4Zwq#s)B{!n5int<3qj-Z~s& zmeG|2Ti&2&MpZD`F*U*$T(6~tMj|Oa9weUL#dq^+%cT?Wu>MCot|~D2K@{rn#)0Yn z2S +mxoHST)hW~Z*wM%W-LcVcQ1Nf0_Q^GAZp z%8DI$h!i1u5B$rt9%0ybyWN;my7!R!a|Qrd;^| Rx%LLgkF zS_reUOgbImwt{D+I@GS(!4bFL#jFLS>|wEJYSgW_fD`2HrgHEYt^R|d@oSxXC=f{f z_l?vXx9e)~LgX^M#eJY6i7@zsoY;IN;Y00&vdjFBd_Irka$rzU^y&%&cWO-k2Yv8W zyf-FNQ$a$=ja|#0YSZ^D=jta`M)QU%R+tpW%QW{(&0RBjL<{o?^GROk?K9nr=I**f zH2t=_X +@cqwB`>1~^aB%z-? zU?dtnf7d%{;I9`OleAC~b2Gy=S8ep!CpvvneWa~#Nrq9dQI=pvYWkls@bgeyU7rpq zRh9qpe_&hW7+2PO^Uv%Uf6f2%v9d68s+T+_VeKx?roJGq4rbVwn0*tX=aarqn_XA0 zg*E;)fieN#EzPbuNrPAK=F8t3RM^k6TuPDPr-iXvO0we0_iVXw@)&t1!aJH>DbCG> z!(Rk@Mj2{D15HaDqU@5EXAOSN;Sx(cXu=L5cWynm!mypM1bNoIjDRI1hJbH_vE=R1 zqW;D@2=ihtUX)i#SgR2`+U$^o!7?qiKU{~YeAs>scHLx{qZXCC`*hqS806){4aee~ z8@hK^?%FNWqs%-dv4s#Ti)py>04#6adel9r*>uhK*yux5k3IWlLiGA}#O-_kKog2! z=*M5e=JRPJ(FsWn4N+DzeiRD~C~Xf%p1YwlcO<%i-+n-K(ul-td9(L1zdh{RAc0g@ z_ACEt4id&|bi`|9=!2C3xi*54<)m5kKl~Liuz#z<1ZcYpMlScpi}0Uw4#nO3{mOAu z7ca3=aA_Y(tt3IP@a8f+7E|~)PKD;e)q#7=ul@AzFkq)kHHE-($M9sWji#j_6t<&& z{%W4I8o5aLPtY`x+TmSFQ<(%-bMxtFdKf_4ecx@e?VbzTd-F+fW9O$wC%IgfC2VvP z^X62{`qTZwcchK@$Y@|{F`Mnd_U{FP)5c3R6aY=zw)m~WIC&i^dM849K!}F`z)EV6 z?j|low@*VIu*_5*(j3759EJ(ETl|t%jys4m;hXp4hqZs%x4@23mvN*6QPg?pmREny z=M5+07z?B=390k(kynk9_q*5<&XU}YfVrtvC5qZd^~@5Avz@+tmyubHxE8-`)Jf!m zpp75Yo1WM}F6WD_)@BVB(dlPrB6CGtH1iA5p>tGe?6n3OYvW!_&ozJ3c@33qN4$bI z;rxzn+rYeocaB%ra|=z8x0eqL%QmZw?5j4oJ)%9<)Ykj70F6WntX--sT)kT0s^fbr zIRQVRc$mvxxh_T!8Lg+M=gx=zPPx66siu{w=Of;eG7gJo^pqeSWd^_g102ltpzN&- zjW @Zl>5m@7$#f8zZU$IVOeJ1Ev! )Oaf1!A<{xx)rAL{GOp ziSgY-_)?Tj4m-=LVtDIw)>Xt0CrFFnFNRxOd6cK94>MC~6y#26V*9V !V__ zd5KfKzMl-t!bm2Cj!edZKN-+%JF9}YJ{G6e;H5u72fPfd^?6=bYA*1AJh3P%&Rvn8 zrXq)qXy65t+j%$d-dK#wV;xXR= g}o1;Zk+Z$ e@_y@b22rZ~M55U-5nszL=6d%Ko(PWo!Vuo{I{${Y)ce zWaad}+rQcLa5)4pC$^c3yxJU927MfJ+Y7*n!H&ar7YGkI<)Ff=FE9C9Ap*B6r0$Q= z7zheNGAT-9es2R@Z#^q)+wPxCvPA%?#mBOi6DJDpx1Hr(?MHJD$u8LfV7-?c#~qJZ zCR)faYi!YvljtzirlvexK%Toz+UVG{CXAI kNsV3cPg0+R<7-+h7-}c$5 zD&ge8eP<#t8r(mxU6Z$4oj5+%A zE8Xv|j))I&jpnG;dAd8jeyY2N?JBe_W;B3{(oU~Di6M8Cuk(0iy9vUE8#JX)ey#Hw z3)9;(@OV~Jtgi_y919r0hL%DJQ#r5?l@#u=+{dYV3o0S5`^c8!?KX0jaw5(?7ya-X z(b7aa8YU98*wFJRH~8%xc@28=u)Vk0f MW}`!u+uhEK?sk^^2Cg2FEVqjZ z_?Zr#WdQkJn2Q|U#dJqxElHchG{2o)38P*53r>3B@iG=;092gKwjSDx+~Fhq)8P^c z?&}rH33~YLwLFy>d_7EHkx8o-u-CE_@w_}XeGRqo2_pVjSKGG(W1%^y+$=gkHv5G- z5S_01gaVg#7c|QB!dNltg8B{3=bdEOOb?V*E#vXnlH{z`bvHEg6oJufH>80=3#!@_ z0W~hWUpjy5e28I#SOkx358@) X(#(XGei{L#`s|_*jl}qO z3b%6vcwy z!pxPdzf0btHfF0DtVC|XmjU+lPEO9py;I*MEQ W8k7i6=<21<(TUGS#jLkW-V+n1D)z2*G}hBBZUSwK zom`dmu8(jV__v2l!U T?M&vYX@RvS*|K@nL>Q{JH zCi|iVer%g^IS{1F d zU2V-V<>}eMVz;GdKb;!*db`8*4z_n4#O=|Z&&2hEA2A(lS}k+Jwz^DFdwsnho5P-5 zKz09b_70{{VoBGEencAcoMz^R(r27_13g1$NAd0fL2b^q^EVGNNoYv_DZjP9i#!mO z8RJ#6P7&|}J@*oq2_()`R(h#cp0$x(OIV9ZleXCor}r&tZI`TWt=OC!Q=0qeC`}|t zVm&rNCRyCVjWYqv8Ls`}Z^>P;`DxaVjg?Wue(UMZCQzFrU#iRA0$8}_ipGdgOI|LX zPL*nR(47DXoTrzghqNlY2zknnQXV{Dv2D>0Mub3SoxF8_tIb7*{Mu|;@U`G|-T-TZ zhD|FJX7mIVLR83=uu-|98`-_c6DU4_?Vs+|TiKHa6@$V7p{o(5T*Zu`yY7>79#gl& zeigVup=OHf)h6XcPmWSj21l=w_ 3tX(7)%}yO7f{8L-d W_=~i#;nx^@dJPI= z;##R0W@w-!PHxHbh=1Iw9b{&~s}wqlxJQH}^iNGiwm7fhI1Xm>d~lRE6h`UcO*Y}s zvpg_l#%OO6u~D(p;uDU#DIffb9IrHnA}9PggbY>|H&)x1ud^+xkP)Dlet+#o|M#k4 zf>23}K3DXcUYS;CWC-1$V_ZW2w9)IVhe5t#peBw7Yn<_PA6Pd-fRI#e5__1-KdZi4 z=E!5G7;fkP)!OqH4V(xB zw}}km9K$)^X={{=IfrwrzMo1_2gQ_@Ao86ab~}R< z8FHK{ziEtQo1`=B0m{}v>< DnerKz&QuSDkz1QtO(e9ReB_hBV;dVY!)4epfpL!`rLp-oyL zdl`>L^`s> lMu`>c@rnVJh |j{m+`i`8C+u#Nu{p_fk39}5uki> z(i7?~f(aaIKloMRP4&lJ-ydiAYvhs_hA`<~3mrQ@$%BFX^aW%q4X &|gdU~Voiy~*{xI9NAG-)+HGgOfTL^fe& z+@RY&5h$VEsdPdo~+zodFjn zL^1e@*H_Q`D%EQ(b{mQumxnVmSvL!mrZpo#GxFDm{>K_brMSHP*Jan&RR2qMRPUgl z9!;;-XkmRexcqsk-Xgehr-j6%H-90!goeGCobXsp3)YY%)mM^|R_=H Cu8d7hqigJMr{qT;>z%L49zF4|u9LlM+8!J?u5Y4(^OJZGNeW|(AkUIm2Xqom zyQ$#wB^pgm%$3lT3Fl@=#CpuiA@1(+`Bjg{q3`Zb$Htx2D|~Njuhj%i&gvn4!325I zJrXR`@T4KzTU9WjNs`11=lizM%8`>D??-zcepGvtTAU!Eyxn5{NWowUyceMIUFZK= zsKQ+C#3~^{PbdI~P4(vrxk7PW)F1={JMq{IWh%pbUgA30)K#4%rITr>_Vx;^o$W#B zO^{|gS5ycuKwXc%d;8R{8T7FEX)W+7t15GGWvb4y!ukcrmzjPXn#95*B$#5~A*|4O zCFW(8Ft0tddZX_c1osk%|9@iw9|IQwkkg3)RMVSXwv%WHA*cTVSvq~x-ty?wk*44Z z!kI7(< {qpi8un*m5|0SVZ?O8aA?S4kZmBa$8zwWpgyWWLKcZFnNw*F+s}Mr zbW4+WoaBWkAGu*VR{gAu(wx_iA!#lh1D8>{v%k=4G(8aqjhkg&H$oQhHD*^0iCz-~ z1JN7_>a N5;QMmYsvzuzczuit>Dat*LT?iL3h06UFj>gTKCmp{u;$=3z;&8X#inTbvn zLiAM?Y4naeJsL(y zCeXjXy zl1@M3(JVk_wb%Tr90OmfqX!Lo z(7E!p==Ko=9_#rI-yX#bMuvE0yL{EegMCb4J|8MdHn6s7#y%Pwk!&UWVU_%fc>?y| zm0V-r7B?78Qm6|N86pkwWx|Y`@IuVX0|>B__-TGBcXea!On%Q#*p(%^@J6Tq@pQ+7 zKtN+ne>!1XaO -X;LqzVr}W`5NAqD zxTw1G8&0Y
E3i3@( zBPowDd1?(EO@y6Qf}e^ kh#YzJfKNGX^f;hygG0P@!vl`=fiTJ9ARlV`q$^-TVebb z2!_8+q6sSO@f)^vMH4#w$%f5-oAA?zzzLkV!~Yw-;xWg9i4+}qMdbx}yOA3^bIbVw zilsm-;{;cjDvmXCH-}E6b9Du40ec5*mF!ER6`jYbDh>&3*!-cX!xHK7N?w2%zoE75 zi^T_Ib7k2 xpj;92+}3B z)6U=(+EatVJ2%`Bd`8J6o|`tq;#8yE#Y)`8KtRf=xr!VN*P*@vr^)fu-i`4@zUP$y zdNP4|Z{B{5JtsHUSEj6jxS^i?y|Jee4W=9vKyB>)+S5B%&)jV2hURT%r3(O^C{*!Y z(Mq6lSf6>V!f`XbAM0U}USzUSdc4mRR@4od%*)S~n8|6EZBi=i;T0)q0k$dXdPE)n zJsYu^v)`r;_Z4XNvO7&(>W`e(F2~hom&V+i;alhPFtk^Hdld<%Vm_y3X$U^8D{4CL zXg^A_jBRBx-)2>uZy(MM)1_6F_xxe+8XoAAe4J8ov(3_BruIGOJeh_O&^BE|^~_gO zNCDYOV$0?hk_mh0U8Pj&qPcF)(AnZ1^2j>eC29Z+^?fclS$JpSX0I!2D5iU#Ah1Nk zB?;zfD6|;v;@+S!u&pH~wL2Iq1Ym?OLt*w#yj##O5r*sU%%rzQE0))VFV!LB{3a8R zqT12arg9CWR^(%=Dbs3wR3x4b5uLw3cB2TI+m$R9E~lWg>zOX7WVc3I0Ppi^a4}wb z%iPKq)s&YCZjK2Dzlbm}Ct=VAJRX;4v|k0{vtA%ATq;)1r=3bLoSW;-I${;qzwHGy zV7f)k0zBF3{X->^pARW?w=LuAbrG_9UaETBFl8i$?C`^vBKRlTbp#R9@jpEhVhp){ z23NaC)ycSH4c>%|&JRZNU~A=2i{u-2OPEv;rxX^2jH1er?nx-cp>hxBc7BiiZIFhy z7bA%%E+F-TCZH$gjwzDlw{H|#uZ4vM_vAp6p&jaO#~=U39r1qlU@>~?ofk$CU1U;0 zB(~t;{>b}86 yWk^^>0wt(r|32tt6KA28m;8J; zuFh5ys{vMuaC=Qjy(j<1tf9LVOQropA?Y6)t=w3c+cV?4;_^Jyuh*i+o>A6-X!u(5 z3QAo@^FLLVPb0B?nxt*w3g2fpCUkFCpjuW(wgwMhduK%w>bEZzH?(DsR+Mas$-9tE zvYT-5^=4IzCJv%L#1Amz=aok0CK9_oo@tY9kCS|tj^N8Vk}9Lc6-$5JYeZx-ZW_G7 z)yRlS1iMM#xNi4edSAa;U#_U%Y>g@JE*q{qZOl4)yS1T0qI;xt+rQ)?LbI%02O~OL zQHe!yuFtj^NIP`Zl6d@Ac8u|pp8Ud0?B99z{rhcKR`b !HB(t6&f7d`=P!LBCOcoUvqrueOZOzNrT}v-)Zd4k)zoLubTFi z?B@5M37+!=jv@aDn9#1};dbMZov)4kO#RLHYN7c+`WG|EPRpB^RN2}xg(8Ua(QUVM z#0~c5+1P%Afjo@36uQ{kw!JPi`O`4;#S(Ulm|RT%Mc}aJFik8;2wmH}bBW}DdcM}a znxX}C&1nS8SDZE32ILxB*xHjcMnIOl4lym(9(f3`Zc zm1&Bl=BQ3(x$Qr=(Jj@NrzchSmvgPVhd`~pEV)kWb$D-W>>Tyr&tzKtEqq~wU#;Cm z!|fEmB%{@zW4fASs=A(7CQJ{2)+9sgaxJBki-?n58X&DAx89|l{%Ai#YlzWo>dX~q z1t&A66osY|o9Cf7;TI0WNFvjdzwaMgMw&EE$TgPJ3oH~#(`y;WZ|d1RNumlfY(QCb z4|fCtLl4!XZ4gLlanm4Fq_~CIf)s FYF1H$Z$EM$YE|Wb z?bnj*QxRTn6)Z4tuDAtIKAi=stB`TY%*%< UeV`j--ZK$7kf25^?@pa1#Fm;)6` zmG-s@yS{7-TPl7@K(jk|oN2T^>jA$@nohFmLbp;xiCncXUWs8^M>V+^L`gjjd^ DYqSNIX{cq=ILS1p1J s(s^+()sZOKBe2~X88d|h-J?C$Qx8haiymRud4_=!F&wDjpE-!-BwmJANRCktSP zkT6y_CW}8d;$rubPZ$c)=A;)6XK N?E+4=#i1?{PuN%L z8ZDG34kg(PHhiag1+u;he*dj?bD-GPMI&0|^QW%4Eej+Go$rB=O(s-}-$UePaJfpY z8wo=|_7B`zLfR=0G%c<0pE9$~HU)6oo~q99{Um2r0-CH7G(N{rL2PJn`M2zGpTR rLh8Gbn*|XM-NAY(r;0CKsq|HL=t?doN4$1PDTBY-Z zRJYRA?W2h#rrZ7kfxg^kpj&r0vjn&8^8%p(mnQu;6_ZHHpzX9JIi}Y WgL1H9jez`f`^8A<3`|o2`HwAt6`v%$|NojYEf-uHFZH1dpSF5aO zNFx5_t5WHmCr0o#(zXl9jxx0qfCE2)$&gN|4 WG5US2|oAL{XKbJeNkpB(P z|JXPGOtnqRP|0~p`sKFeY|%2D->q8m0(kJ o%n8K>)c@k}FI$AA zvYb!udzD>CnjpLX`cW$)IRav+`AJ`3i)fncnvR}``ihl5KDFgnd&_C)9Wwsx;jf%R zRP=heC$?W1f?gD~=|@=naY30 kUlYIobxKN%CJmV+w|Rz=fqX@`I<}X8kS6B>}oCWJWl~*(-wltAirmIEe4m3 z(z(ihMEe4PW3F%(CF}1?a4ZEKUamN_X{>IWZ<7m~EDJcEAH&Ep+};V7ODP-f_Wk;+ zT@8=>iH{r9!i?Y_*W_D)yO-aSXJ=+`7S275#yf5tyd1U?rxGe A<-J3ypv9OH4#s=mkLMDJM9D3W=hG?D6C_3kD;>TsR!T^$cYuyi0*6eQDX2>ZHG z^a<}**IhWK)7kq-vffwo>pf;eknxr4N{|Y`Dd26ki)%j3X|mKb8ra#smHD0XdWljb zJ?-Q4^$+U>v*b?$-*lXBEVNy(S5^zf-sIHhH&Sq%?T04YcOO7pENivf;~8tk4OVk` zV#NT-Vgo&GO!3)N1V|;=W_g> 28CRv7t zXeMzzpZ5YzSS+lGZwbUnA?$+6B)4EQ&GCP%5V-;)OENFzu;~H@jk!_Y0}*MxopmUZ z=%UGYw0aq>dLln^Ir56up2C+DYtsPMQlAI63S$VxsXsmiZdCg|vl~LJZ@l})W=wLH zUy(QR^@Z6;LJ17m36P!7{mjr~sPExj w`*6QMcyhj=)y(xr} z#Akk9u!XuaG@tV9<1^&06+J8{yy=mxZo`9hz7Dsx2PGy59)Y8VO(KptxuLDZ)RER^ z3rbrYaP2bT-E{k0%H8BWn$$R$j~m_Dnm{v=eSQ@4?GbSaonCt>Os@f+X{+Cedoq?= z4a_#p^K^yIis65%av@R=hG`8yHM;AvnKmc6*9Wm?NTx8vhR!a0tK>0dUS+xn`$ou^ ztno5ItCp~uh94Al7?@XXu%}-5t}ey`od(@iFurWJYbsyH?6UE_&x+-7+&YR><4R5T zcmZ+-uJ5fNbsQ2AVzMK5;G_ttX8O(NFLVT0X**+l7O#Y_M|=@~4{_0XU>1dC8eZ0( z3YryD?0eADVM zgyCfdEb<0|323RLvYEY+W2cDxptY^SYrEfdD+)!quwQ0oBZ$+{R7$rMDIXKhQq z=iugi$Z|M+>5#qUc1Zgs0S;2OLhr4c^2?3do6s@fp}Dq~U9Y}J^fS`c#y%@0;Iq-| z;?+Ct*<@ne+Q7=C`iP_cqo;pEw(IlKdNcLUu6LW!+vXx`< l((g5ATH)Z>*D_ z oOCF8gHMYU7qFFM}WWW!Ixud{_kDO?eK@%=PmkX zck3d!07s;!g^y2n^vzax6!%4s%P0G!zvjSHD{=*!5g@gI02_@5^~I?`!IdWZ`$9cO zaef#0w*8XI)ro$}^`_{uK5T_{XcSJyiZ35%i{U ;yH z#dx^eEL<5}lg8)#15dj%_S2&b7|!sp#&vqK^AJc|a8AN;07$*W+wWw*9zhlZm-p7W zz^#>l{G$*0c$MjQ(901W%Yk8#^eV#A4gBaFJPWM_{)gDAIOpmL=Bq(*f8XTob?tWg ztLFrDYTD69(d^=99%xE%$)cLbM7ZxV)T}9zB-f`K7n~A0kMIc$=b|M!`h*$MJn{hK z8ug+uYF}n1(_t`@bcLdzKZw(Gkpnvms%wOxS# f1@Ss weBJASvqA+iZ zse+L617HK4D+Tzlxjz~V Rj`CD3Jk7nDs-_=M_qUp)DtA#pd?c$gqmdzilNH-L9wclL8i*? zVJLtmj~Yjcilc6L@LgS}5j|CUs3GNCD+oEWu{F|E#R`mhGX2NZC#DH3-Wr=^Oz?Tc zh32-^l)~A1Xr0y_B9V6mwwd57?h1~O7maMDdH`OkQ{-eHc4_a_`e+ngI_KUi7_N7a z3A0 {yIM(+|m zxVeX`MlU!Xi&`@$_it7YFT*)Ql9!Eg?ff48*a32XzRCCLgP~>6`Xz;c^D|V;rsWy} znX;7FOtF512nM@xmhX3ZO>F%vR(|lT>uOO@sWAfoOG@zn;C4tDAr}wyJ0Ye}fc(|4 z7DP=5Gd&6vBi`Om57YdRhhq#x?em&ym?P|I+}feq*>|MSY5dd&!i+Lwk#w=XqZr9o z1n#Dbe3<_sOCzXf^V1J=HnK2ti=3eKM}d>$_MmWtsZ)hTIH*+*Tp8*Y*OIXi &$RZ`8MK8JtW!TrZT6q)kbM$L)@*QT|tN((w*mO@|La E|eNo`JACr5hE gkrlw;;LFBV`eUi_et zoA!+>zV1b}Is3shA;QJl=^(tbdS6l@F!3FU#%Tru<&>^V!;sp$cGwKtXnC^$^J!~V z9vYO&mwfg!E)*AAz!l8CDO}6XgqE_GH~vq|vLqy^ITeZov4|6%#3Uy*-16-XZug1! z2v{G9{vrj^S;9W5-Z+0&0)=FZ9i6=(7eLnecc87w>l7Bbq@Ajr$1zi5wSX&^_!Fpn zY$}AsE-vz}^dNbXSKzF0jcRad9H!-;3!S&03n)u7OitSUw2+diQR4k}(->(BRB3sT ziADjXE<8c~l8~LzJx1k)aqu5<=9K7m0BZl_94Ou4q>279Ix$B}^4>0P8be*kF;d4` zA-hNWvzy7%pc6hL9U(!=p}a;YVf(0D@}>HKVWzz0$pK FwqTL3Ac7YqY%|0ij*6H1Ky@Q$8)tN>(;z1} ze*UM#k}Fcfhd8xJm=pw~ cN%?j5*1PtSVZ32c9I@ mM7KR% z9la5dy|>WRl^0xpOv3)uVSDZ+;kFT~uVpij5=X8Yr#UrSnSrt3a n0jtuZZa zJSo)FL&;X|V?}GgTKqY?Z%K{nh+d0M-vk5Fju$30;)z$(I!i=bnWF!&QkWcs5H10r zXbkw9Q2%&dv@@r999lU3Iv6_v0c%&Go~#D^yqnJr#Qj{6pUvpIGtz%uNsDsRoKaTN zBVKHvkt0UY;_SD5KX?H<(>gH%(JI0}4~d>?uEzNo=K$n=+#FprdWW~`VeGs_2A^_N zqY{6@(V~k8rjzg*ymfY#t7xlp+0*Ts{bY>3v68bhh>Zam+%M1LP}9jQJH1RF!2qx@ z){~@J <66t3DkwXH{I;hnSY`^ycL z8v8v_yDBpm-sW%O5QM5<_SBNM$7%Mi(BS&VcK zm&LNSMyC64 aF$@3>KJ6SLo2|a`m3CWjLO*Pc!Jgtn6;qm~P%i4zYzrX0@5$ zdXK{@ThGd08hKPM8<4o`eKX@0S&8Y!n^hrPQV-fvJiL>;o(vtW6G%+JCh7A{uPL2O z@(-!jft8*wapBZWSL+Lwk=W>C8;~@OWBcAT>oXyqud9Nyhyu#cT*m3e*T##=z&Vjg zSx6(PW_iv=vGnFg$!uF3j-LSmHB@m>t1b Z~#pOv20&R_D ;@6$T*2*Uc#FXt0;>w%6q+5n* zA|D{A7|D}WIV74NokOXXX*CMyfisgM8u M{$O^*72C!@=e2= UUsSm$)h!-Ob+H8I^Ig$&eyn+6=3HM*_?w3Zd zTqpR??L3f|?EC01ikjKk8^nNYo#oCI#Kpj>W^ew*z!?46d%W5!fxQE;H5cEbuvbQ$ z)+0fmo;jvae%>wNd`*Y`A3p~pZA0+_z%=}RweEZe*Q~<}K5&B%&?a;$@zjLsNsdV) zf$+OB7nMQ;<$Gc-D1!9Ed&7{Q1?7YH8b>p8yISk6dE(@4t2_xs(;z2X`9Wpbv66(d ze_;34Q7<0 )(BmL1%e$+&P0(49I I!ZzN}9nEfi0fq4aJdfUdWYxNU8Ggdj` zc-g(@!&(fk6*5=S9?$4^H+FPbQfTMYpqoODTK)FZ {jPu^c4RsFGC$`w$L`-1M^2Dl5(KIhAGpV2&2i78) z9Tu5CoSScPH8DFS?32K9+!{#jRwzaVI*$>96!{;>YkGQR*Eg?(9R%%AHCC6_iyMNj zO-vHb>**;e` )UkpiwaR_#j{OmIVNP#9jt&FoVQ}Q-E(*1 {N$d35DoiMmMxzH zF_flycCHqa&_5Sf2$~^fz_PA#`0S5 oF*pT#<@@nwr zua#q>OGynD7|`@*6+bpKBo?K=SDi~`sOeF)jU);ahprx_Ka} YUrzv-BxZ38r z3V-`FgqWSCe*Y1gBTWmiIlllnJOzV`SM${aWkHR4(UoKQYK_2ZVi$MuadL^)1P%-D zkdnPL_x<)iMPF^h7Lb!iCQ%+q)20lZ%)>FO#(C2r%mI8S6L-Gp9Lz{0fhT{Cy;ba6 zD-5PK(kdTgZ(a{r+t$1D^QTBf Mcjn&l(dD$<^y RkuuClkB-}wr7Vn} z3|KX>4#9OB<>zh!rSy1KrpyX+?DzK2WK9B@o#m@!W2`4jafWw4%dOge6YmcpBa@XD z{bIl)5JR2jfkuX_^G)j?wweg2?{5!DWaZt|KDow#wgM0S7aaUhZWZ(qxjSs08!LW! za?Mdt(vpwMp~wV&`w;r5u-F6^D*`G$^q}v-&~GgBpV$zTcH&6?5Vsawt_KqppfN9? z+ C=`^(i#zt%8JcG^Rx>gI3U7%Vxy2#>1 za6JVIv8!vHi39O>l4)}x`HEH2`U91aR|@>rUF8tAp&p3=f*&P#On|F!sS#EQIf`?^ z;EarA^>CJQ{##sy%o(nb_+1crb38nU6_m)5s87So9?+XyLMZf-nGyZzg*tIx_NU(o zWGtgVI@0}=mg|V0(M?B42UKH`KbGLSku7@@8ippmjT+bQ=Kfu#E{=JD7Qh;(K5(x_ zf6|pjF)LdO&o@0tD>EA&WITHPJtA)g{E?VqU5C>V?v4fECNv6c&|vr)2CIu+%Ll7f zh6}*s%l~>Q%x_MK0%(BlAYHgWt%9hm9L71y@T=rr%Jw}GHT;=H#h ^^(qk~$0~CMP60YgZ5~0S;kr@1*AF z%eLIJpPAG-1BEr&2A*(9S)?{Z4M@dji%{h;ffCl4>7>7SkKWC>p_Qq9#i(bRK n9b9tn~&BIsD7t9~r**#~%HmcTIk8{i|{_1a?3{}}Hu`I6L^I*5Tz z#f`g$NH0H19Y&|SG)>Tw??||$j2t2BMKUI{$#3J;r5&-R(97i!Er~r)M9HJv3a30E z4Os6Y)cP5NWk?nY8jU#)%`A*#W8|UAP8qYJN@Amth(?2YuZJ{kKKtlW=tty$@Of?t zs4RhcC=^Pd)`ud=_R@bkKN4Bunca%HsOrb8cM=%?yffTxJ>r6yA9=`{Kzw2vi+var zX<>Czt*oROcy#cem63Q9PRh$CDkgU>-6H5D#0hd8`HD&+dWH&;S-f>?evB?N<-dda z3QR 5O*mjW2Z0 +f#NF}s_+u| zCB+}Y*xRbH2RbA7(EDmtClENJDogV?cKl4)357FJIgFtBX%nBncr_)(XM*L5GVqwG z_i&!p)Eci{?8vPmW`JwZ(m#ix9bNdfU4R8{yBT=~m~E0+AD-s2!u9lz+msLEG)8`d z6;_DHmXa{7S{g~Q=*-f})t3K)>xcNvJE+pywGfk D zsJlTB?)p$qTzjC%kW7+yYn0{e(_}sdJ*2S-@Za9#t&Yw;Ipu7NuGM;Hy8m9& &|~+LZLNB9r{!?H)KqUiH*+&q<3r zahNvl%*$NyGN~|`O!6WU6TYc;H<{9LahUfdgL81n%mqe$&R@!cs}A=i1)1l0;G3>k zEryZQ7qPxqL~6%JLui-eHoJ5<|5bF3T@o0JtbwiPYxpq*;1%1_M%DSf7abaReYDE? zU~o+9N+`zPY5cnIkbg%Y=?(54SFHO0RG#XuSW#l6lI|#iAxQmylR& ~O?q)=r!;v$wjU-GdfxF^ z!S*h0)(3!ITV@~J)ss3eMl|oevtAON1mgYkQ@Q97<4s@tzjycJHr r+WvOL9RLFhIFqy9Vhl z{SFfAS|@od{)~Sz9~Q$V+)b_M_t^c2+oiRdh_i9Vt?S~Yog(Zte9IX^?FAEWGtfQuQYtEO zaKw+4Uh?v9a9P}7aL~q{7PPsQ9NYK&UocC#SSnNSgO#9Eyi(>55iYY-pxqNz`PNec zIl3rP*IW*!wTt`f`{W#F>~Sk2dViSYeEz7mEW~(1bK|rnlE6=z2V5RbIFW&H0?d&| zg_Q$M9%{~LM1qvAygqE(Q7) D)E1EBDHp13g zZ34WBmM Er2Q?NIS zsU3?CWBLI`zEL;dLMbr7Z<7b1qA!w?)+-L5r}NKo%_~&3!{yQdMlLT-TIO@4Va%L}3UmlDGHQPdezL{a!tPdlA%f*$1wzW&XV08=@+a=S zp&c=WWu_o9Mu=Tf_(|y3yr4p}k=5{}JGiVm500*kpgV%PE}~jt3-t9k%6z{>@*O;` zvCMh_yZ{ISIkUJC#8QiHZE~Ml=;!HM3-B!;=}_rycjkoNqZ>l|@*+OiC>G@-NZ)l) zZERP@-@Nm@xwmle-ETf%%Oz{`Cz}#{tY}?6MBiAR(Eq1M?WLD6DRn^Z<`sekW56rl z>wH`6?|Z6?W}g^hVxB)R7G{^CMI|p05bryT{#JX~t`KN7^lGRoHT5`Urp6bpU%Z!{ z^8dksY<_=-Y;EUua;m3k<$E8(;Y+!OXMT)B>SdDpL_5&Xsa)|CLW4r33p+mSAJjE+ zRT-O;(#PPn