- // ...
- session_handle sql = soci_create_session("postgresql://dbname=mydb");
+// ...
+session_handle sql = soci_create_session("postgresql://dbname=mydb");
- statement_handle st = soci_create_statement(sql);
+statement_handle st = soci_create_statement(sql);
- soci_use_int(st, "id");
- soci_set_use_int(st, "id", 123);
+soci_use_int(st, "id");
+soci_set_use_int(st, "id", 123);
- int namePosition = soci_into_string(st);
+int namePosition = soci_into_string(st);
- soci_prepare(st, "select name from persons where id = :id");
+soci_prepare(st, "select name from persons where id = :id");
- soci_execute(st, true);
+soci_execute(st, true);
- char const * name = soci_get_into_string(st, namePosition);
+char const * name = soci_get_into_string(st, namePosition);
- printf("name is %s\n", name);
-
- soci_destroy_statement(st);
- soci_destroy_session(sql);
+printf("name is %s\n", name);
+soci_destroy_statement(st);
+soci_destroy_session(sql);
+```
The *simple* interface supports single and bulk data exchange for static binding.
Dynamic row description is not supported in this release.
See [Simple client interface](/reference.html#simpleclient) reference documentation for more details.
-#### Low-level backend interface
+## Low-level backend interface
The low-level backend interface allows to interact with backends directly and in principle allows to access the database without involving any other component.
There is no particular reason to use this interface in the user code.
diff --git a/docs/languages/ada/concepts.md b/docs/languages/ada/concepts.md
index 7a7c66ab..97af5c3b 100644
--- a/docs/languages/ada/concepts.md
+++ b/docs/languages/ada/concepts.md
@@ -1,4 +1,4 @@
-# SOCI-Ada - manual
+# SOCI-Ada Bindings
## Concepts
@@ -12,7 +12,6 @@ There are two kinds of objects that can be managed by the SOCI-Ada library:
* *Use elements*, which are data objects that are transferred from the user program to the database as parameters of the query (and, if supported by the target database, that can be modified by the database server and transferred back to the user program). There are single use elements for binding parameters of single-row queries and vector use elements for binding whole bunches of data for transfer. The use elements are identified by their *name*.
-
The user program can read the current value of into and use elements and assign new values to use elements. Elements are strongly typed and the following types are currently supported:
* `String`
@@ -29,4 +28,4 @@ All statements are handled within the context of some *session*, which also supp
Sessions can be managed in isolation or as a group called *connection pool*, which helps to decouple tasking design choices from the concurrency policies at the database connection level. Sessions are *leased* from the pool for some time during which no other task can access them and returned back when no longer needed, where they can be acquired again by other tasks.
-All potential problems are signalled via exceptions that have some descriptive message attached to them.
\ No newline at end of file
+All potential problems are signalled via exceptions that have some descriptive message attached to them.
diff --git a/docs/languages/ada/idioms.md b/docs/languages/ada/idioms.md
index 034e6ab2..fe9e9c57 100644
--- a/docs/languages/ada/idioms.md
+++ b/docs/languages/ada/idioms.md
@@ -1,6 +1,4 @@
-# SOCI-Ada - manual
-
-## Common Idioms
+# SOCI-Ada Bindings - Idioms
As any other library, SOCI-Ada has its set of idioms that ensure optimal work in terms of performance and resource usage. Still, the optimal use will depend on the concrete usage scenario - the places where programmer choices are needed will be described explicitly.
@@ -10,251 +8,228 @@ The idioms below are provided as *complete programs* with the intent to make the
This type of query is useful for DDL commands and can be executed directly on the given session, without explicit statement management.
+```ada
+with SOCI;
- with SOCI;
+procedure My_Program is
- procedure My_Program is
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
- SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+begin
- begin
+ SQL.Execute ("drop table some_table");
- SQL.Execute ("drop table some_table");
+end My_Program;
- end My_Program;
-
-
-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.
-
-
+```
+Note: The session object is initialized by a constructor function call. An alternative would be to declare it without initialization and later use the `Open` operation to establish a physical connection with the database.
## Simple query without parameters resulting in one row of data
This type of query requires only single into elements, which together with the statement have to be manipulated explicitly.
+```ada
+with SOCI;
+with Ada.Text_IO;
- with SOCI;
- with Ada.Text_IO;
+procedure My_Program is
- procedure My_Program is
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
+ Pos : SOCI.Into_Position;
- SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
- St : SOCI.Statement := SOCI.Make_Statement (SQL);
- Pos : SOCI.Into_Position;
+ Num_Of_Persons : SOCI.DB_Integer;
- Num_Of_Persons : SOCI.DB_Integer;
+begin
- begin
+ Pos := St.Into_Integer;
+ St.Prepare ("select count(*) from persons");
+ St.Execute (True);
- Pos := St.Into_Integer;
- St.Prepare ("select count(*) from persons");
- St.Execute (True);
+ Num_Of_Persons := St.Get_Into_Integer (Pos);
- Num_Of_Persons := St.Get_Into_Integer (Pos);
+ Ada.Text_IO.Put_Line ("Number of persons: " & SOCI.DB_Integer'Image (Num_Of_Persons));
- Ada.Text_IO.Put_Line ("Number of persons: " & SOCI.DB_Integer'Image (Num_Of_Persons));
-
- end My_Program;
-
-
-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.
-
-
+end My_Program;
+```
+Note: The into element is inspected by providing the position value that was obtained at the time if was created. No operations are defined for the position type. There can be many into elements with a single query.
## Simple query with parameters and without results
This type of query requires only use elements.
- with SOCI;
+```ada
+with SOCI;
- procedure My_Program is
+procedure My_Program is
- SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
- St : SOCI.Statement := SOCI.Make_Statement (SQL);
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
- begin
+begin
- St.Use_Integer ("increase");
- St.Set_Use_Integer ("increase", 1000);
+ St.Use_Integer ("increase");
+ St.Set_Use_Integer ("increase", 1000);
- St.Prepare ("update persons set salary = salary + :increase");
- St.Execute (True);
+ St.Prepare ("update persons set salary = salary + :increase");
+ St.Execute (True);
- end My_Program;
-
-
-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.
-
-
+end My_Program;
+```
+Note: The "`:increase`" in the query is a placeholder variable. There can be many such variables and each of them needs to be filled in by respective use element.
## Repeated query with parameters and without results
This type of query requires only use elements, but they can be set differently for each statement execution.
+```ada
+with SOCI;
- with SOCI;
+procedure My_Program is
- procedure My_Program is
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
- SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
- St : SOCI.Statement := SOCI.Make_Statement (SQL);
+begin
- begin
+ St.Use_String ("name");
- St.Use_String ("name");
+ St.Prepare ("insert into countries(country_name) values(:name)");
- St.Prepare ("insert into countries(country_name) values(:name)");
+ St.Set_Use_String ("name", "Poland");
+ St.Execute (True);
- St.Set_Use_String ("name", "Poland");
- St.Execute (True);
+ St.Set_Use_String ("name", "Switzerland");
+ St.Execute (True);
- St.Set_Use_String ("name", "Switzerland");
- St.Execute (True);
+ St.Set_Use_String ("name", "France");
+ St.Execute (True);
- St.Set_Use_String ("name", "France");
- St.Execute (True);
+end My_Program;
+```
- end My_Program;
-
-
-Note:
-
-Each time the query is executed, the *current* values of use elements are transferred to the database.
-
-
+Note: Each time the query is executed, the *current* values of use elements are transferred to the database.
## Batch query with parameters and without results
This type of query requires vector use elements. Compare with the previous example.
- with SOCI;
+```ada
+with SOCI;
- procedure My_Program is
+procedure My_Program is
- SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
- St : SOCI.Statement := SOCI.Make_Statement (SQL);
- First : SOCI.Vector_Index;
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
+ First : SOCI.Vector_Index;
- use type SOCI.Vector_Index;
+ use type SOCI.Vector_Index;
- begin
+begin
- St.Use_Vector_String ("name");
+ St.Use_Vector_String ("name");
- St.Use_Vectors_Resize (3);
+ St.Use_Vectors_Resize (3);
- First := St.Use_Vectors_First_Index;
+ First := St.Use_Vectors_First_Index;
- St.Set_Use_Vector_String ("name", First + 0, "Poland");
- St.Set_Use_Vector_String ("name", First + 1, "Switzerland");
- St.Set_Use_Vector_String ("name", First + 2, "France");
+ St.Set_Use_Vector_String ("name", First + 0, "Poland");
+ St.Set_Use_Vector_String ("name", First + 1, "Switzerland");
+ St.Set_Use_Vector_String ("name", First + 2, "France");
- St.Prepare ("insert into countries(country_name) values(:name)");
- St.Execute (True);
+ St.Prepare ("insert into countries(country_name) values(:name)");
+ St.Execute (True);
- end My_Program;
+end My_Program;
+```
-
-Note:
+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:
+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;
+```ada
+with SOCI;
+with Ada.Text_IO;
- procedure My_Program is
+procedure My_Program is
- SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
- St : SOCI.Statement := SOCI.Make_Statement (SQL);
- Pos : SOCI.Into_Position;
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
+ Pos : SOCI.Into_Position;
- begin
+begin
- Pos := St.Into_String;
+ Pos := St.Into_String;
- St.Prepare ("select country_name from countries");
- St.Execute;
+ St.Prepare ("select country_name from countries");
+ St.Execute;
- while St.Fetch loop
+ while St.Fetch loop
- Ada.Text_IO.Put_Line (St.Get_Into_String (Pos));
+ Ada.Text_IO.Put_Line (St.Get_Into_String (Pos));
- end loop;
+ end loop;
- end My_Program;
+end My_Program;
+```
-
-Note:
+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;
+```ada
+with SOCI;
+with Ada.Text_IO;
- procedure My_Program is
+procedure My_Program is
- SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
- St : SOCI.Statement := SOCI.Make_Statement (SQL);
- Pos : SOCI.Into_Position;
+ SQL : SOCI.Session := SOCI.Make_Session ("postgresql://dbname=my_database");
+ St : SOCI.Statement := SOCI.Make_Statement (SQL);
+ Pos : SOCI.Into_Position;
- Batch_Size : constant := 10;
+ Batch_Size : constant := 10;
- begin
+begin
- Pos := St.Into_Vector_String;
- St.Into_Vectors_Resize (Batch_Size);
+ Pos := St.Into_Vector_String;
+ St.Into_Vectors_Resize (Batch_Size);
- St.Prepare ("select country_name from countries");
- St.Execute;
+ St.Prepare ("select country_name from countries");
+ St.Execute;
- while St.Fetch loop
+ while St.Fetch loop
- for I in St.Into_Vectors_First_Index .. St.Into_Vectors_Last_Index loop
+ for I in St.Into_Vectors_First_Index .. St.Into_Vectors_Last_Index loop
- Ada.Text_IO.Put_Line (St.Get_Into_Vector_String (Pos, I));
+ Ada.Text_IO.Put_Line (St.Get_Into_Vector_String (Pos, I));
- end loop;
+ end loop;
- St.Into_Vectors_Resize (Batch_Size);
+ St.Into_Vectors_Resize (Batch_Size);
- end loop;
+ end loop;
- end My_Program;
+end My_Program;
+```
-
-##### Note:
+Note:
The loop above is nested. The outer `while` loop fetches consecutive batches of rows from the database with requested batch size; the returned batch can be smaller than requested (the into vector elements are downsized automatically if needed) and the intended batch size is requested again before repeating the `Fetch` operation. For each returned batch, the into vector elements are inspected in the inner `for` loop. This scheme ensures correct operation independently on the size of returned batch and is therefore a recommended idiom for efficiently returning many rows of data.
@@ -262,7 +237,6 @@ There is a tradeoff between efficiency and memory usage and this tradeoff is con
This type of query can have simple (not vectors) parameters that are fixed at execution time.
-
-##### Final note:
+## Final note
Follow good database usage principles and avoid constructing queries by concatenating strings computed at run-time. Thanks to a good type system Ada is much better in preventing various SQL-injection attacks than weaker languages like PHP, but there is still a potential for vulnerability or at least performance loss. As a rule of thumb, rely on *use elements* to parameterize your queries and to provide clean separation between data and code. This will prevent many security vulnerabilities and will allow some servers to optimize their work by reusing already cached execution plans.
diff --git a/docs/languages/ada/index.md b/docs/languages/ada/index.md
index 4fd7921b..fd0deb1f 100644
--- a/docs/languages/ada/index.md
+++ b/docs/languages/ada/index.md
@@ -22,9 +22,9 @@ The SOCI-Ada library offers the following features to the Ada community:
Currently the following database servers are directly supported via their native interfaces:
- * Oracle
- * PostgreSQL
- * MySQL
+* Oracle
+* PostgreSQL
+* MySQL
Other backends exist in the SOCI Git repository and can be provided with future version of the library.
@@ -36,4 +36,4 @@ In order to use SOCI-Ada, compile the C++ parts first (core and required backend
The SOCI-Ada library itself is a single package named `SOCI`. This package can be just imported in the target project as is or pre-built to the binary form if required.
-In order to link the user programs the `-lsoci_core -lstdc++` linker options need to be provided on the Unix/Linux platforms.
\ No newline at end of file
+In order to link the user programs the `-lsoci_core -lstdc++` linker options need to be provided on the Unix/Linux platforms.
diff --git a/docs/languages/ada/reference.md b/docs/languages/ada/reference.md
index 9650eb99..8c3cdec3 100644
--- a/docs/languages/ada/reference.md
+++ b/docs/languages/ada/reference.md
@@ -1,4 +1,4 @@
-# SOCI-Ada - manual
+# SOCI-Ada Bindings - Reference
## API Reference
@@ -6,142 +6,147 @@ The SOCI-Ada library is entirely implemented as a single package named `SOCI`. A
The following describes all publicly visible elements of this package:
+```ada
+--
+-- General exception related to database and library usage.
+--
- --
- -- General exception related to database and library usage.
- --
-
- Database_Error : exception;
+Database_Error : exception;
+```
Each problem related to the interaction with the database or to the incorrect usage of the library itself is signalled by raising this exception. Each occurrence of this exception has some human-readable error message that can be obtained by a call to `Ada.Exceptions.Exception_Message`.
- --
- -- Session.
- --
+```ada
+--
+-- Session.
+--
- type Session is tagged limited private;
+type Session is tagged limited private;
- not overriding
- function Make_Session (Connection_String : in String) return Session;
+not overriding
+function Make_Session (Connection_String : in String) return Session;
- not overriding
- procedure Open (This : in out Session; Connection_String : in String);
+not overriding
+procedure Open (This : in out Session; Connection_String : in String);
- not overriding
- procedure Close (This : in out Session);
+not overriding
+procedure Close (This : in out Session);
- not overriding
- function Is_Open (This : in Session) return Boolean;
+not overriding
+function Is_Open (This : in Session) return Boolean;
+```
The `Session` object can exist in two states: "connected" (or "open") and "disconnected". It can be created as connected at initialization time with a call to the constructor function `Make_Session` or left default-initialized in the disconnected state and later changed to connected with `Open` (the latter option is the only that is available in the Ada 95 version of the library). `Session` objects can be also associated with the connection pool, see below.
The `Connection_String` should have the form `"backendname://parameters"`, where `backendname` is used to construct the name of the dynamically loadable library that will be used to provide specific database services. Backends included in the current distribution of the main SOCI library are:
-
* `oracle` (implemented as `libsoci_oracle.so` or `libsoci_oracle.dll`)
* `postgresql` (implemented as `libsoci_postgresql.so` or `libsoci_postgresql.dll`)
* `mysql` (implemented as `libsoci_mysql.so` or `libsoci_mysql.dll`)
-
Other backends can be added to the library in the future or by the user himself, please see the documentation of the main SOCI library for details.
The `parameters` component of the `Connection_String` depends on the given backend, please see the documentation of the main SOCI project for the meaning and recognized options. The web pages related to the backends above are:
-* Oracle: * [http://soci.sourceforge.net/doc/backends/oracle.html](http://soci.sourceforge.net/doc/backends/oracle.html" target="_blank)
-* PostgreSQL: * [http://soci.sourceforge.net/doc/backends/postgresql.html](http://soci.sourceforge.net/doc/backends/postgresql.html" target="_blank)
-* MySQL: * [http://soci.sourceforge.net/doc/backends/mysql.html](http://soci.sourceforge.net/doc/backends/mysql.html" target="_blank)
+* [Oracle](http://soci.sourceforge.net/doc/backends/oracle.html)
+* [PostgreSQL](http://soci.sourceforge.net/doc/backends/postgresql.html)
+* [MySQL](http://soci.sourceforge.net/doc/backends/mysql.html)
The `Open` operation can be called only in the disconnected state (which changes the state of `Session` object to connected). The `Close` operation can be called in any state (provided that the session is not associated with the connection pool, see below) and after that the `Session` is in the disconnected state.
`Session` objects are closed automatically as part of their finalization. If the `Session` object is associated with the connection pool, the finalizer detaches from the pool without closing the connection.
+```ada
+-- Transaction management.
- -- Transaction management.
+not overriding
+procedure Start (This : in Session);
- not overriding
- procedure Start (This : in Session);
+not overriding
+procedure Commit (This : in Session);
- not overriding
- procedure Commit (This : in Session);
-
- not overriding
- procedure Rollback (This : in Session);
+not overriding
+procedure Rollback (This : in Session);
+```
These operations handle transactions. The exact meaning of transactions and whether transactions are automatic for some kinds of statements (and which ones) depend on the target database.
-
- -- Immediate query execution.
- not overriding
- procedure Execute (This : in Session; Query : in String);
+```ada
+-- Immediate query execution.
+not overriding
+procedure Execute (This : in Session; Query : in String);
+```
This operation allows to create implicit statement, prepare it for the given `Query` and execute it.
- --
- -- Connection pool management.
- --
+```ada
+--
+-- Connection pool management.
+--
- type Connection_Pool (Size : Positive) is tagged limited private;
+type Connection_Pool (Size : Positive) is tagged limited private;
- not overriding
- procedure Open
- (This : in out Connection_Pool;
- Position : in Positive;
- Connection_String : in String);
+not overriding
+procedure Open
+ (This : in out Connection_Pool;
+ Position : in Positive;
+ Connection_String : in String);
- not overriding
- procedure Close (This : in out Connection_Pool; Position : in Positive);
+not overriding
+procedure Close (This : in out Connection_Pool; Position : in Positive);
- not overriding
- procedure Lease (This : in out Connection_Pool; S : in out Session'Class);
+not overriding
+procedure Lease (This : in out Connection_Pool; S : in out Session'Class);
+```
The `Connection_Pool` encapsulates a fixed-size collection of sessions. Individual sessions are indexed from `1` to `Size` (provided as discriminant) and can be `Open`ed and `Close`d explicitly. Each connection in the pool can be created with different `Connection_String`, if needed.
-
The `Lease` operation allows to associate a given `Session` object (that has to be in the disconnected state itself) with one connection from the pool. The pool guarantees that at most one task can lease a given connection from the pool. If there are no free connections in the pool, the `Lease` operation will block waiting until some connection is freed.
The `Session` object that is associated with a connection from the pool automatically gives it back to pool as part of the `Session`'s finalizer. There is no other way to "detach" from the pool.
----
-#####Note:
+Note:
It is assumed that the lifetime of `Connection_Pool` encloses the lifetimes of all `Session` objects that are leased from it. There is no particular protection against it and it is possible to construct a code example with allocators that create partially overlapping `Connection_Pool` and `Session`, but this is considered obscure and not representative to the actual use scenarios. To avoid any potential problems, create `Connection_Pool` in the scope that encloses the scopes of leased `Session`s.
----
- --
- -- Statement.
- --
+```ada
+--
+-- Statement.
+--
- type Statement (<>) is tagged limited private;
+type Statement (<>) is tagged limited private;
- type Data_State is (Data_Null, Data_Not_Null);
+type Data_State is (Data_Null, Data_Not_Null);
- type Into_Position is private;
+type Into_Position is private;
- type Vector_Index is new Natural;
+type Vector_Index is new Natural;
+```
The `Statement` type and supporting types. `Data_State` is used to indicate null values in the database sense - each value of into or use elements has a state from this type.
+```ada
+-- Statement preparation and execution.
- -- Statement preparation and execution.
+not overriding
+procedure Prepare (This : in Statement; Query : in String);
- not overriding
- procedure Prepare (This : in Statement; Query : in String);
+not overriding
+procedure Execute
+ (This : in Statement;
+ With_Data_Exchange : in Boolean := False);
- not overriding
- procedure Execute
- (This : in Statement;
- With_Data_Exchange : in Boolean := False);
+not overriding
+function Execute
+ (This : in Statement;
+ With_Data_Exchange : in Boolean := False) return Boolean;
- not overriding
- function Execute
- (This : in Statement;
- With_Data_Exchange : in Boolean := False) return Boolean;
+not overriding
+function Fetch (This : in Statement) return Boolean;
- not overriding
- function Fetch (This : in Statement) return Boolean;
-
- not overriding
- function Got_Data (This : in Statement) return Boolean;
+not overriding
+function Got_Data (This : in Statement) return Boolean;
+```
The `Prepare` operation needs to be called before any other operation in the above group and it prepares the execution for the given `Query`. No into and use elements can be created after this operation is called.
@@ -151,19 +156,20 @@ The `Fetch` function is used to transfer next portion of data (a single row or a
The `Got_Data` function returns `True` if the last execution or fetch resulted in some data being transmitted from the database server.
+```ada
+--
+-- Data items handling.
+--
- --
- -- Data items handling.
- --
+-- Database-specific types.
+-- These types are most likely identical to standard Integer,
+-- Long_Long_Integer and Long_Float, but are defined distinctly
+-- to avoid interfacing problems with other compilers.
- -- Database-specific types.
- -- These types are most likely identical to standard Integer,
- -- Long_Long_Integer and Long_Float, but are defined distinctly
- -- to avoid interfacing problems with other compilers.
-
- type DB_Integer is new Interfaces.C.int;
- type DB_Long_Long_Integer is new Interfaces.Integer_64;
- type DB_Long_Float is new Interfaces.C.double;
+type DB_Integer is new Interfaces.C.int;
+type DB_Long_Long_Integer is new Interfaces.Integer_64;
+type DB_Long_Float is new Interfaces.C.double;
+```
The data types used for interaction with the database are:
@@ -173,92 +179,88 @@ The data types used for interaction with the database are:
* `DB_Long_Float`, defined above
* `Ada.Calendar.Time`
- -- Creation of single into elements.
+```ada
+-- Creation of single into elements.
- not overriding
- function Into_String (This : in Statement) return Into_Position;
+not overriding
+function Into_String (This : in Statement) return Into_Position;
- not overriding
- function Into_Integer (This : in Statement) return Into_Position;
+not overriding
+function Into_Integer (This : in Statement) return Into_Position;
- not overriding
- function Into_Long_Long_Integer (This : in Statement) return Into_Position;
+not overriding
+function Into_Long_Long_Integer (This : in Statement) return Into_Position;
- not overriding
- function Into_Long_Float (This : in Statement) return Into_Position;
-
- not overriding
- function Into_Time (This : in Statement) return Into_Position;
+not overriding
+function Into_Long_Float (This : in Statement) return Into_Position;
+not overriding
+function Into_Time (This : in Statement) return Into_Position;
+```
These functions instruct the library to create internal simple into elements of the relevant type. They return the position of the into element, which can be later used to identify it.
----
-#####Note:
+Note: Simple into elements cannot be created together with vector into elements for the same statement.
-Simple into elements cannot be created together with vector into elements for the same statement.
+Note: Simple into elements cannot be created together with vector into elements for the same statement.
----
+```ada
+-- Inspection of single into elements.
----
-#####Note:
-Simple into elements cannot be created together with vector into elements for the same statement.
+not overriding
+function Get_Into_State
+ (This : in Statement;
+ Position : in Into_Position)
+ return Data_State;
----
+not overriding
+function Get_Into_String
+ (This : in Statement;
+ Position : in Into_Position)
+ return String;
- -- Inspection of single into elements.
+not overriding
+function Get_Into_Integer
+ (This : in Statement;
+ Position : in Into_Position)
+ return DB_Integer;
- not overriding
- function Get_Into_State
- (This : in Statement;
- Position : in Into_Position)
- return Data_State;
+not overriding
+function Get_Into_Long_Long_Integer
+ (This : in Statement;
+ Position : in Into_Position)
+ return DB_Long_Long_Integer;
- not overriding
- function Get_Into_String
- (This : in Statement;
- Position : in Into_Position)
- return String;
+not overriding
+function Get_Into_Long_Float
+ (This : in Statement;
+ Position : in Into_Position)
+ return DB_Long_Float;
- not overriding
- function Get_Into_Integer
- (This : in Statement;
- Position : in Into_Position)
- return DB_Integer;
-
- not overriding
- function Get_Into_Long_Long_Integer
- (This : in Statement;
- Position : in Into_Position)
- return DB_Long_Long_Integer;
-
- not overriding
- function Get_Into_Long_Float
- (This : in Statement;
- Position : in Into_Position)
- return DB_Long_Float;
-
- not overriding
- function Get_Into_Time
- (This : in Statement;
- Position : in Into_Position)
- return Ada.Calendar.Time;
+not overriding
+function Get_Into_Time
+ (This : in Statement;
+ Position : in Into_Position)
+ return Ada.Calendar.Time;
+```
These functions allow to inspect the state and value of the simple into element identified by its position. If the state of the given element is `Data_Null`, the data-reading functions raise exceptions for that element.
- -- Inspection of vector into elements.
+```ada
+-- Inspection of vector into elements.
- not overriding
- function Get_Into_Vectors_Size (This : in Statement) return Natural;
+not overriding
+function Get_Into_Vectors_Size (This : in Statement) return Natural;
- not overriding
- function Into_Vectors_First_Index (This : in Statement) return Vector_Index;
+not overriding
+function Into_Vectors_First_Index (This : in Statement) return Vector_Index;
- not overriding
- function Into_Vectors_Last_Index (This : in Statement) return Vector_Index;
+not overriding
+function Into_Vectors_Last_Index (This : in Statement) return Vector_Index;
- not overriding
- procedure Into_Vectors_Resize (This : in Statement; New_Size : in Natural);
+not overriding
+procedure Into_Vectors_Resize (This : in Statement; New_Size : in Natural);
+```
The `Get_Into_Vectors_Size` returns the number of entries in any of the vector into elements for the given statement.
@@ -266,163 +268,161 @@ The `Into_Vectors_First_Index` returns the lowest index value for vector into el
The `Into_Vectors_Resize` procedure allows to change the size of all use vectors for the given statement.
- not overriding
- function Get_Into_Vector_State
- (This : in Statement;
- Position : in Into_Position;
- Index : in Vector_Index)
- return Data_State;
+```ada
+not overriding
+function Get_Into_Vector_State
+ (This : in Statement;
+ Position : in Into_Position;
+ Index : in Vector_Index)
+ return Data_State;
- not overriding
- function Get_Into_Vector_String
- (This : in Statement;
- Position : in Into_Position;
- Index : in Vector_Index)
- return String;
+not overriding
+function Get_Into_Vector_String
+ (This : in Statement;
+ Position : in Into_Position;
+ Index : in Vector_Index)
+ return String;
- not overriding
- function Get_Into_Vector_Integer
- (This : in Statement;
- Position : in Into_Position;
- Index : in Vector_Index)
- return DB_Integer;
+not overriding
+function Get_Into_Vector_Integer
+ (This : in Statement;
+ Position : in Into_Position;
+ Index : in Vector_Index)
+ return DB_Integer;
- not overriding
- function Get_Into_Vector_Long_Long_Integer
- (This : in Statement;
- Position : in Into_Position;
- Index : in Vector_Index)
- return DB_Long_Long_Integer;
+not overriding
+function Get_Into_Vector_Long_Long_Integer
+ (This : in Statement;
+ Position : in Into_Position;
+ Index : in Vector_Index)
+ return DB_Long_Long_Integer;
- not overriding
- function Get_Into_Vector_Long_Float
- (This : in Statement;
- Position : in Into_Position;
- Index : in Vector_Index)
- return DB_Long_Float;
+not overriding
+function Get_Into_Vector_Long_Float
+ (This : in Statement;
+ Position : in Into_Position;
+ Index : in Vector_Index)
+ return DB_Long_Float;
- not overriding
- function Get_Into_Vector_Time
- (This : in Statement;
- Position : in Into_Position;
- Index : in Vector_Index)
- return Ada.Calendar.Time;
+not overriding
+function Get_Into_Vector_Time
+ (This : in Statement;
+ Position : in Into_Position;
+ Index : in Vector_Index)
+ return Ada.Calendar.Time;
+```
These functions allow to inspect the state and value of the vector use element identified by its position and index. If the state of the given element is `Data_Null`, the data-reading functions raise exceptions for that element.
+```ada
+-- Creation of single use elements.
- -- Creation of single use elements.
+not overriding
+procedure Use_String (This : in Statement; Name : in String);
- not overriding
- procedure Use_String (This : in Statement; Name : in String);
+not overriding
+procedure Use_Integer (This : in Statement; Name : in String);
- not overriding
- procedure Use_Integer (This : in Statement; Name : in String);
+not overriding
+procedure Use_Long_Long_Integer (This : in Statement; Name : in String);
- not overriding
- procedure Use_Long_Long_Integer (This : in Statement; Name : in String);
+not overriding
+procedure Use_Long_Float (This : in Statement; Name : in String);
- not overriding
- procedure Use_Long_Float (This : in Statement; Name : in String);
-
- not overriding
- procedure Use_Time (This : in Statement; Name : in String);
+not overriding
+procedure Use_Time (This : in Statement; Name : in String);
+```
These functions instruct the library to create internal simple use elements of the relevant type, identified by the given `Name`.
----
-##### Note:
-Simple use elements cannot be created together with vector use elements for the same statement.
+Note:
-Vector use elements cannot be created together with any into elements for the same statement.
+* Simple use elements cannot be created together with vector use elements for the same statement.
+* Vector use elements cannot be created together with any into elements for the same statement.
----
+```ada
+-- Creation of vector use elements.
+not overriding
+procedure Use_Vector_String (This : in Statement; Name : in String);
- -- Creation of vector use elements.
+not overriding
+procedure Use_Vector_Integer (This : in Statement; Name : in String);
- not overriding
- procedure Use_Vector_String (This : in Statement; Name : in String);
+not overriding
+procedure Use_Vector_Long_Long_Integer (This : in Statement; Name : in String);
- not overriding
- procedure Use_Vector_Integer (This : in Statement; Name : in String);
-
- not overriding
- procedure Use_Vector_Long_Long_Integer (This : in Statement; Name : in String);
-
- not overriding
- procedure Use_Vector_Long_Float (This : in Statement; Name : in String);
-
- not overriding
- procedure Use_Vector_Time (This : in Statement; Name : in String);
+not overriding
+procedure Use_Vector_Long_Float (This : in Statement; Name : in String);
+not overriding
+procedure Use_Vector_Time (This : in Statement; Name : in String);
+```
These functions instruct the library to create internal vector use elements of the relevant type, identified by the given `Name`.
+Note:
----
-#####Note:
+* Simple use elements cannot be created together with vector use elements for the same statement.
+* Vector use elements cannot be created together with any into elements for the same statement.
-Simple use elements cannot be created together with vector use elements for the same statement.
+```ada
+-- Modifiers for single use elements.
-Vector use elements cannot be created together with any into elements for the same statement.
+not overriding
+procedure Set_Use_State
+ (This : in Statement;
+ Name : in String;
+ State : in Data_State);
----
+not overriding
+procedure Set_Use_String
+ (This : in Statement;
+ Name : in String;
+ Value : in String);
- -- Modifiers for single use elements.
+not overriding
+procedure Set_Use_Integer
+ (This : in Statement;
+ Name : in String;
+ Value : in DB_Integer);
- not overriding
- procedure Set_Use_State
- (This : in Statement;
- Name : in String;
- State : in Data_State);
+not overriding
+procedure Set_Use_Long_Long_Integer
+ (This : in Statement;
+ Name : in String;
+ Value : in DB_Long_Long_Integer);
- not overriding
- procedure Set_Use_String
- (This : in Statement;
- Name : in String;
- Value : in String);
+not overriding
+procedure Set_Use_Long_Float
+ (This : in Statement;
+ Name : in String;
+ Value : in DB_Long_Float);
- not overriding
- procedure Set_Use_Integer
- (This : in Statement;
- Name : in String;
- Value : in DB_Integer);
-
- not overriding
- procedure Set_Use_Long_Long_Integer
- (This : in Statement;
- Name : in String;
- Value : in DB_Long_Long_Integer);
-
- not overriding
- procedure Set_Use_Long_Float
- (This : in Statement;
- Name : in String;
- Value : in DB_Long_Float);
-
- not overriding
- procedure Set_Use_Time
- (This : in Statement;
- Name : in String;
- Value : in Ada.Calendar.Time);
+not overriding
+procedure Set_Use_Time
+ (This : in Statement;
+ Name : in String;
+ Value : in Ada.Calendar.Time);
+```
These operations allow to modify the state and value of simple use elements. Setting the value of use element automatically sets its state to `Data_Not_Null`.
+```ada
+-- Modifiers for vector use elements.
- -- Modifiers for vector use elements.
+not overriding
+function Get_Use_Vectors_Size (This : in Statement) return Natural;
- not overriding
- function Get_Use_Vectors_Size (This : in Statement) return Natural;
+not overriding
+function Use_Vectors_First_Index (This : in Statement) return Vector_Index;
- not overriding
- function Use_Vectors_First_Index (This : in Statement) return Vector_Index;
+not overriding
+function Use_Vectors_Last_Index (This : in Statement) return Vector_Index;
- not overriding
- function Use_Vectors_Last_Index (This : in Statement) return Vector_Index;
-
- not overriding
- procedure Use_Vectors_Resize (This : in Statement; New_Size : in Natural);
+not overriding
+procedure Use_Vectors_Resize (This : in Statement; New_Size : in Natural);
+```
The `Get_Use_Vectors_Size` returns the number of entries in any of the vector use elements for the given statement.
@@ -430,94 +430,95 @@ The `Use_Vectors_First_Index` returns the lowest index value for vector use elem
The `Use_Vectors_Resize` procedure allows to change the size of all use vectors for the given statement.
+```ada
+not overriding
+procedure Set_Use_Vector_State
+ (This : in Statement;
+ Name : in String;
+ Index : in Vector_Index;
+ State : in Data_State);
- not overriding
- procedure Set_Use_Vector_State
- (This : in Statement;
- Name : in String;
- Index : in Vector_Index;
- State : in Data_State);
+not overriding
+procedure Set_Use_Vector_String
+ (This : in Statement;
+ Name : in String;
+ Index : in Vector_Index;
+ Value : in String);
- not overriding
- procedure Set_Use_Vector_String
- (This : in Statement;
- Name : in String;
- Index : in Vector_Index;
- Value : in String);
+not overriding
+procedure Set_Use_Vector_Integer
+ (This : in Statement;
+ Name : in String;
+ Index : in Vector_Index;
+ Value : in DB_Integer);
- not overriding
- procedure Set_Use_Vector_Integer
- (This : in Statement;
- Name : in String;
- Index : in Vector_Index;
- Value : in DB_Integer);
+not overriding
+procedure Set_Use_Vector_Long_Long_Integer
+ (This : in Statement;
+ Name : in String;
+ Index : in Vector_Index;
+ Value : in DB_Long_Long_Integer);
- not overriding
- procedure Set_Use_Vector_Long_Long_Integer
- (This : in Statement;
- Name : in String;
- Index : in Vector_Index;
- Value : in DB_Long_Long_Integer);
-
- not overriding
- procedure Set_Use_Vector_Long_Float
- (This : in Statement;
- Name : in String;
- Index : in Vector_Index;
- Value : in DB_Long_Float);
-
- not overriding
- procedure Set_Use_Vector_Time
- (This : in Statement;
- Name : in String;
- Index : in Vector_Index;
- Value : in Ada.Calendar.Time);
+not overriding
+procedure Set_Use_Vector_Long_Float
+ (This : in Statement;
+ Name : in String;
+ Index : in Vector_Index;
+ Value : in DB_Long_Float);
+not overriding
+procedure Set_Use_Vector_Time
+ (This : in Statement;
+ Name : in String;
+ Index : in Vector_Index;
+ Value : in Ada.Calendar.Time);
+```
These operations allow to modify the state and value of vector use elements. Setting the value of use element automatically sets its state to `Data_Not_Null`.
+```ada
+-- Inspection of single use elements.
+--
+-- Note: Use elements can be modified by the database if they
+-- are bound to out and inout parameters of stored procedures
+-- (although this is not supported by all database backends).
+-- This feature is available only for single use elements.
- -- Inspection of single use elements.
- --
- -- Note: Use elements can be modified by the database if they
- -- are bound to out and inout parameters of stored procedures
- -- (although this is not supported by all database backends).
- -- This feature is available only for single use elements.
+not overriding
+function Get_Use_State
+ (This : in Statement;
+ Name : in String)
+ return Data_State;
- not overriding
- function Get_Use_State
- (This : in Statement;
- Name : in String)
- return Data_State;
+not overriding
+function Get_Use_String
+ (This : in Statement;
+ Name : in String)
+ return String;
- not overriding
- function Get_Use_String
- (This : in Statement;
- Name : in String)
- return String;
+not overriding
+function Get_Use_Integer
+ (This : in Statement;
+ Name : in String)
+ return DB_Integer;
- not overriding
- function Get_Use_Integer
- (This : in Statement;
- Name : in String)
- return DB_Integer;
+not overriding
+function Get_Use_Long_Long_Integer
+ (This : in Statement;
+ Name : in String)
+ return DB_Long_Long_Integer;
- not overriding
- function Get_Use_Long_Long_Integer
- (This : in Statement;
- Name : in String)
- return DB_Long_Long_Integer;
+not overriding
+function Get_Use_Long_Float
+ (This : in Statement;
+ Name : in String)
+ return DB_Long_Float;
- not overriding
- function Get_Use_Long_Float
- (This : in Statement;
- Name : in String)
- return DB_Long_Float;
+not overriding
+function Get_Use_Time
+ (This : in Statement;
+ Name : in String)
+ return Ada.Calendar.Time;
+```
- not overriding
- function Get_Use_Time
- (This : in Statement;
- Name : in String)
- return Ada.Calendar.Time;
-
-These functions allow to inspect the state and value of the simple use element identified by its name. If the state of the given element is `Data_Null`, the data-reading functions raise exceptions for that element.
\ No newline at end of file
+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.
diff --git a/docs/license.md b/docs/license.md
index a2670535..18aef88a 100644
--- a/docs/license.md
+++ b/docs/license.md
@@ -4,7 +4,7 @@ The SOCI library is distributed under the terms of the [Boost Software License](
## Boost Software License
-*Version 1.0 - August 17th, 2003*
+Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
diff --git a/docs/multithreading.md b/docs/multithreading.md
index 614f1d4c..7fcd51ae 100644
--- a/docs/multithreading.md
+++ b/docs/multithreading.md
@@ -8,28 +8,28 @@ Depending on the design of the client application this might be also the most st
For some applications, however, it might be preferable to decouple the set of threads from the set of sessions, so that they can be optimized separately with different resources in mind.
The `connection_pool` class is provided for this purpose:
+```cpp
+// phase 1: preparation
- // phase 1: preparation
+const size_t poolSize = 10;
+connection_pool pool(poolSize);
- const size_t poolSize = 10;
- connection_pool pool(poolSize);
+for (size_t i = 0; i != poolSize; ++i)
+{
+ session & sql = pool.at(i);
- for (size_t i = 0; i != poolSize; ++i)
- {
- session & sql = pool.at(i);
+ sql.open("postgresql://dbname=mydb");
+}
- sql.open("postgresql://dbname=mydb");
- }
+// phase 2: usage from working threads
- // phase 2: usage from working threads
+{
+ session sql(pool);
- {
- session sql(pool);
-
- sql << "select something from somewhere...";
-
- } // session is returned to the pool automatically
+ sql << "select something from somewhere...";
+} // session is returned to the pool automatically
+```
The `connection_pool`'s constructor expects the size of the pool and internally creates an array of `session`s in the disconnected state.
Later, the `at` function provides *non-synchronized* access to each element of the array.
@@ -44,4 +44,4 @@ This way, the connection pool guarantees that its session objects are never used
Note that the above scheme is the simplest way to use the connection pool, but it is also constraining in the fact that the `session`'s constructor can *block* waiting for the availability of some entry in the pool.
For more demanding users there are also low-level functions that allow to lease sessions from the pool with timeout on wait.
-Please consult the [reference](reference.html) for details.
\ No newline at end of file
+Please consult the [reference](reference.html) for details.
diff --git a/docs/procedures.md b/docs/procedures.md
index 5cea2200..b7473b93 100644
--- a/docs/procedures.md
+++ b/docs/procedures.md
@@ -2,18 +2,17 @@
The `procedure` class provides a convenient mechanism for calling stored procedures:
- sql << "create or replace procedure echo(output out varchar2,"
- "input in varchar2) as "
- "begin output := input; end;";
-
- std::string in("my message");
- std::string out;
- procedure proc = (sql.prepare << "echo(:output, :input)",
- use(out, "output"),
- use(in, "input"));
- proc.execute(true);
- assert(out == "my message");
+```cpp
+sql << "create or replace procedure echo(output out varchar2,"
+ "input in varchar2) as "
+ "begin output := input; end;";
+std::string in("my message");
+std::string out;
+procedure proc = (sql.prepare << "echo(:output, :input)", use(out, "output"), use(in, "input"));
+proc.execute(true);
+assert(out == "my message");
+```
## Portability note
diff --git a/docs/queries.md b/docs/queries.md
index 6b451078..65f12d93 100644
--- a/docs/queries.md
+++ b/docs/queries.md
@@ -4,20 +4,25 @@
In many cases, the SQL query is intended to be executed only once, which means that statement parsing and execution can go together. The `session` class provides a special `once` member, which triggers parsing and execution of such one-time statements:
- sql.once << "drop table persons";
+```cpp
+sql.once << "drop table persons";
+```
For shorter syntax, the following form is also allowed:
- sql << "drop table persons";
+```cpp
+sql << "drop table persons";
+```
The IOStream-like interface is exactly what it looks like, so that the statement text can be composed of many parts, involving anything that is *streamable* (including custom classes, if they have appropriate `operator<<`):
+```cpp
+string tableName = "persons";
+sql << "drop table " << tableName;
- string tableName = "persons";
- sql << "drop table " << tableName;
-
- int id = 123;
- sql << "delete from companies where id = " << id;
+int id = 123;
+sql << "delete from companies where id = " << id;
+```
## Query transformation
@@ -31,46 +36,51 @@ For one-time statements, query transformation is performed before each execution
A few short examples how to use query transformation:
-*defined as free function:*
+* defined as free function:
- std::string less_than_ten(std::string query)
+```cpp
+std::string less_than_ten(std::string query)
+{
+ return query + " WHERE price < 10";
+}
+
+session sql(postgresql, "dbname=mydb");
+sql.set_query_transformation(less_than_ten);
+sql << "DELETE FROM item";
+```
+
+* defined as function object:
+
+```cpp
+struct order : std::unary_function
- #include
- #include
- #include
- #include
+```cpp
+#include "soci.h"
+#include "soci-oracle.h"
+#include
+#include
+#include
+#include
+#include
- using namespace soci;
- using namespace std;
+using namespace soci;
+using namespace std;
- bool get_name(string &name) {
- cout << "Enter name: ";
- return cin >> name;
- }
+bool get_name(string &name) {
+ cout << "Enter name: ";
+ return cin >> name;
+}
- int main()
+int main()
+{
+ try
{
- try
+ session sql(oracle, "service=mydb user=john password=secret");
+
+ int count;
+ sql << "select count(*) from phonebook", into(count);
+
+ cout << "We have " << count << " entries in the phonebook.\n";
+
+ string name;
+ while (get_name(name))
{
- session sql(oracle, "service=mydb user=john password=secret");
+ string phone;
+ indicator ind;
+ sql << "select phone from phonebook where name = :name",
+ into(phone, ind), use(name);
- int count;
- sql << "select count(*) from phonebook", into(count);
-
- cout << "We have " << count << " entries in the phonebook.\n";
-
- string name;
- while (get_name(name))
+ if (ind == i_ok)
{
- string phone;
- indicator ind;
- sql << "select phone from phonebook where name = :name",
- into(phone, ind), use(name);
-
- if (ind == i_ok)
- {
- cout << "The phone number is " << phone << '\n';
- }
- else
- {
- cout << "There is no phone for " << name << '\n';
- }
+ cout << "The phone number is " << phone << '\n';
+ }
+ else
+ {
+ cout << "There is no phone for " << name << '\n';
}
}
- catch (exception const &e)
- {
- cerr << "Error: " << e.what() << '\n';
- }
}
+ catch (exception const &e)
+ {
+ cerr << "Error: " << e.what() << '\n';
+ }
+}
+```
diff --git a/docs/statements.md b/docs/statements.md
index 730ee61b..c2b9f418 100644
--- a/docs/statements.md
+++ b/docs/statements.md
@@ -4,17 +4,19 @@
Consider the following examples:
- // Example 1.
- for (int i = 0; i != 100; ++i)
- {
- sql << "insert into numbers(value) values(" << i << ")";
- }
+```cpp
+// Example 1.
+for (int i = 0; i != 100; ++i)
+{
+ sql << "insert into numbers(value) values(" << i << ")";
+}
- // Example 2.
- for (int i = 0; i != 100; ++i)
- {
- sql << "insert into numbers(value) values(:val)", use(i);
- }
+// Example 2.
+for (int i = 0; i != 100; ++i)
+{
+ sql << "insert into numbers(value) values(:val)", use(i);
+}
+```
Both examples will populate the table `numbers` with the values from `0` to `99`.
@@ -26,14 +28,16 @@ In fact, more complicated queries are likely to suffer in terms of lower perform
The following example uses the class `statement` explicitly, by preparing the statement only once and repeating its execution with changing data (note the use of `prepare` member of `session` class):
- int i;
- statement st = (sql.prepare <<
- "insert into numbers(value) values(:val)",
- use(i));
- for (i = 0; i != 100; ++i)
- {
- st.execute(true);
- }
+```cpp
+int i;
+statement st = (sql.prepare <<
+ "insert into numbers(value) values(:val)",
+ use(i));
+for (i = 0; i != 100; ++i)
+{
+ st.execute(true);
+}
+```
The `true` parameter given to the `execute` method indicates that the actual data exchange is wanted, so that the meaning of the whole example is
@@ -57,35 +61,41 @@ The `rowset` itself can be used only with select queries.
The following example creates an instance of the `rowset` class and binds query results into elements of `int` type - in this query only one result column is expected.
After executing the query the code iterates through the query result using `rowset_iterator`:
- rowset rs = (sql.prepare << "select values from numbers");
+```cpp
+rowset rs = (sql.prepare << "select values from numbers");
- for (rowset::const_iterator it = rs.begin(); it != rs.end(); ++it)
- {
- cout << *it << '\n';
- }
+for (rowset::const_iterator it = rs.begin(); it != rs.end(); ++it)
+{
+ cout << *it << '\n';
+}
+```
Another example shows how to retrieve more complex results, where `rowset` elements are of type `row` and therefore use [dynamic bindings](exchange.html#dynamic):
- // person table has 4 columns
+```cpp
+// person table has 4 columns
- rowset rs = (sql.prepare << "select id, firstname, lastname, gender from person");
+rowset rs = (sql.prepare << "select id, firstname, lastname, gender from person");
- // iteration through the resultset:
- for (rowset::const_iterator it = rs.begin(); it != rs.end(); ++it)
- {
- row const& row = *it;
+// iteration through the resultset:
+for (rowset::const_iterator it = rs.begin(); it != rs.end(); ++it)
+{
+ row const& row = *it;
- // dynamic data extraction from each row:
- cout << "Id: " << row.get(0) << '\n'
- << "Name: " << row.get(1) << " " << row.get(2) << '\n'
- << "Gender: " << row.get(3) << endl;
- }
+ // dynamic data extraction from each row:
+ cout << "Id: " << row.get(0) << '\n'
+ << "Name: " << row.get(1) << " " << row.get(2) << '\n'
+ << "Gender: " << row.get(3) << endl;
+}
+```
The `rowset_iterator` can be used with standard algorithms as well:
- rowset rs = (sql.prepare << "select firstname from person");
+```cpp
+rowset rs = (sql.prepare << "select firstname from person");
- std::copy(rs.begin(), rs.end(), std::ostream_iterator(std::cout, "\n"));
+std::copy(rs.begin(), rs.end(), std::ostream_iterator(std::cout, "\n"));
+```
Above, the query result contains a single column which is bound to `rowset` element of type of `std::string`.
All records are sent to standard output using the `std::copy` algorithm.
@@ -99,24 +109,24 @@ The following example presents how to insert 100 records in 4 batches.
It is also important to note, that size of vector remains equal in every batch interaction.
This ensures vector is not reallocated and, what's crucial for the bulk trick, new data should be pushed to the vector before every call to `statement::execute`:
- // Example 3.
- void fill_ids(std::vector& ids)
- {
- for (std::size_t i = 0; i < ids.size(); ++i)
- ids[i] = i; // mimics source of a new ID
- }
+```cpp
+// Example 3.
+void fill_ids(std::vector& ids)
+{
+ for (std::size_t i = 0; i < ids.size(); ++i)
+ ids[i] = i; // mimics source of a new ID
+}
- const int BATCH_SIZE = 25;
- std::vector ids(BATCH_SIZE);
+const int BATCH_SIZE = 25;
+std::vector ids(BATCH_SIZE);
- statement st = (sql.prepare <<
- "insert into numbers(value) values(:val)",
- use(ids));
- for (int i = 0; i != 4; ++i)
- {
- fill_ids(ids);
- st.execute(true);
- }
+statement st = (sql.prepare << "insert into numbers(value) values(:val)", use(ids));
+for (int i = 0; i != 4; ++i)
+{
+ fill_ids(ids);
+ st.execute(true);
+}
+```
Given batch size is 25, this example should insert 4 x 25 = 100 records.
@@ -124,15 +134,15 @@ Given batch size is 25, this example should insert 4 x 25 = 100 records.
It is also possible to read all the numbers written in the above examples:
- int i;
- statement st = (sql.prepare <<
- "select value from numbers order by value",
- into(i));
- st.execute();
- while (st.fetch())
- {
- cout << i << '\n';
- }
+```cpp
+int i;
+statement st = (sql.prepare << "select value from numbers order by value", into(i));
+st.execute();
+while (st.fetch())
+{
+ cout << i << '\n';
+}
+```
In the above example, the `execute` method is called with the default parameter `false`.
This means that the statement should be executed, but the actual data exchange will be performed later.
@@ -144,32 +154,35 @@ The above code example should be treated as an idiomatic way of reading many row
It is further possible to select records in batches into `std::vector` based types, with the size of the vector specifying the number of records to retrieve in each round trip:
- std::vector valsOut(100);
- sql << "select val from numbers", into(valsOut);
+```cpp
+std::vector 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 valsOut(BATCH_SIZE);
- statement st = (sql.prepare <<
- "select value from numbers",
- into(valsOut));
- st.execute();
- while (st.fetch())
+```cpp
+const int BATCH_SIZE = 30;
+std::vector valsOut(BATCH_SIZE);
+statement st = (sql.prepare <<
+ "select value from numbers",
+ into(valsOut));
+st.execute();
+while (st.fetch())
+{
+ std::vector::iterator pos;
+ for(pos = valsOut.begin(); pos != valsOut.end(); ++pos)
{
- std::vector::iterator pos;
- for(pos = valsOut.begin(); pos != valsOut.end(); ++pos)
- {
- cout << *pos << '\n';
- }
-
- valsOut.resize(BATCH_SIZE);
+ cout << *pos << '\n';
}
+ valsOut.resize(BATCH_SIZE);
+}
+```
+
Assuming there are 100 rows returned by the query, the above code will retrieve and print all of them.
Since the output vector was created with size 30, it will take (at least) 4 calls to `fetch()` to retrieve all 100 values.
Each call to `fetch()` can potentially resize the vector to a size less than its initial size - how often this happens depends on the underlying database implementation.
@@ -189,47 +202,48 @@ Actually, all supported backends guarantee that the requested number of rows wil
This means that the manual vector resizing is in practice not needed - the vector will keep its size until the end of rowset.
The above idiom, however, is provided with future backends in mind, where the constant size of the vector might be too expensive to guarantee and where allowing `fetch` to down-size the vector even before reaching the end of rowset might buy some performance gains.
-
## Statement caching
Some backends have some facilities to improve statement parsing and compilation to limit overhead when creating commonly used query.
But for backends that does not support this kind optimization you can keep prepared statement and use it later with new references.
To do such, prepare a statement as usual, you have to use `exchange` to bind new variables to statement object, then `execute` statement and finish by cleaning bound references with `bind_clean_up`.
- sql << "CREATE TABLE test(a INTEGER)";
+```cpp
+sql << "CREATE TABLE test(a INTEGER)";
+
+{
+ // prepare statement
+ soci::statement stmt = (db.prepare << "INSERT INTO numbers(value) VALUES(:val)");
{
- // prepare statement
- soci::statement stmt = (db.prepare << "INSERT INTO numbers(value) VALUES(:val)");
+ // first insert
+ int a0 = 0;
- {
- // first insert
- int a0 = 0;
+ // update reference
+ stmt.exchange(soci::use(a0));
- // update reference
- stmt.exchange(soci::use(a0));
-
- stmt.define_and_bind();
- stmt.execute(true);
- stmt.bind_clean_up();
- }
-
- {
- // come later, second insert
- int a1 = 1;
-
- // update reference
- stmt.exchange(soci::use(a1));
-
- stmt.define_and_bind();
- stmt.execute(true);
- stmt.bind_clean_up();
- }
+ stmt.define_and_bind();
+ stmt.execute(true);
+ stmt.bind_clean_up();
}
{
- std::vector v(10);
- db << "SELECT value FROM numbers", soci::into(v);
- for (int i = 0; i < v.size(); ++i)
- std::cout << "value " << i << ": " << v[i] << std::endl;
+ // come later, second insert
+ int a1 = 1;
+
+ // update reference
+ stmt.exchange(soci::use(a1));
+
+ stmt.define_and_bind();
+ stmt.execute(true);
+ stmt.bind_clean_up();
}
+}
+
+{
+ std::vector v(10);
+ db << "SELECT value FROM numbers", soci::into(v);
+ for (int i = 0; i < v.size(); ++i)
+ std::cout << "value " << i << ": " << v[i] << std::endl;
+}
+```
diff --git a/docs/structure.md b/docs/structure.md
index 92dd907b..b1f5bf45 100644
--- a/docs/structure.md
+++ b/docs/structure.md
@@ -15,12 +15,13 @@ The core part of the library and the backend interface definition are placed in
Everything in SOCI is declared in the namespace `soci`. All code examples presented in this documentation assume that your code begins with something like:
+```cpp
+#include "soci.h"
+// other includes if necessary
- #include "soci.h"
- // other includes if necessary
+using namespace soci;
- using namespace soci;
+// ...
+```
- // ...
-
-*Note:* In simple programs, `#include` for the relevant backend is needed only in the file where the `session` object is created with explicit name of the backend factory. The example program on the [previous page](index.html) shows the appropriate `#include` directive for the Oracle backend. It is also possible to name backends at run-time as part of the connection string, in which case no backend-specific `#include` directive is necessary.
+Note: In simple programs, `#include` for the relevant backend is needed only in the file where the `session` object is created with explicit name of the backend factory. The example program on the [previous page](index.html) shows the appropriate `#include` directive for the Oracle backend. It is also possible to name backends at run-time as part of the connection string, in which case no backend-specific `#include` directive is necessary.
diff --git a/docs/transactions.md b/docs/transactions.md
index aaa34186..fc250859 100644
--- a/docs/transactions.md
+++ b/docs/transactions.md
@@ -8,34 +8,38 @@ The SOCI library provides the following members of the `session` class for trans
In addition to the above there is a RAII wrapper that allows to associate the transaction with the given scope of code:
- class transaction
- {
- public:
- explicit transaction(session & sql);
+```cpp
+class transaction
+{
+public:
+ explicit transaction(session & sql);
- ~transaction();
+ ~transaction();
- void commit();
- void rollback();
+ void commit();
+ void rollback();
- private:
- // ...
- };
+private:
+ // ...
+};
+```
The object of class `transaction` will roll back automatically when the object is destroyed
(usually as a result of leaving the scope) *and* when the transaction was not explicitly committed before that.
A typical usage pattern for this class might be:
- {
- transaction tr(sql);
+```cpp
+{
+ transaction tr(sql);
- sql << "insert into ...";
- sql << "more sql queries ...";
- // ...
+ sql << "insert into ...";
+ sql << "more sql queries ...";
+ // ...
- tr.commit();
- }
+ tr.commit();
+}
+```
With the above pattern the transaction is committed only when the code successfully reaches the end of block.
If some exception is thrown before that, the scope will be left without reaching the final statement and the transaction object will automatically roll back in its destructor.
diff --git a/docs/types.md b/docs/types.md
index c8b838f1..3308b94f 100644
--- a/docs/types.md
+++ b/docs/types.md
@@ -45,43 +45,45 @@ Once the data type for each column is known, the data can be formatted appropria
For example, the code below creates an XML document from a selected row of data from an arbitrary table:
- row r;
- sql << "select * from some_table", into(r);
+```cpp
+row r;
+sql << "select * from some_table", into(r);
- std::ostringstream doc;
- doc << "" << std::endl;
- for(std::size_t i = 0; i != r.size(); ++i)
+std::ostringstream doc;
+doc << "" << std::endl;
+for(std::size_t i = 0; i != r.size(); ++i)
+{
+ const column_properties & props = r.get_properties(i);
+
+ doc << '<' << props.get_name() << '>';
+
+ switch(props.get_data_type())
{
- const column_properties & props = r.get_properties(i);
-
- doc << '<' << props.get_name() << '>';
-
- switch(props.get_data_type())
- {
- case dt_string:
- doc << r.get(i);
- break;
- case dt_double:
- doc << r.get(i);
- break;
- case dt_integer:
- doc << r.get(i);
- break;
- case dt_long_long:
- doc << r.get(i);
- break;
- case dt_unsigned_long_long:
- doc << r.get(i);
- break;
- case dt_date:
- std::tm when = r.get(i);
- doc << asctime(&when);
- break;
- }
-
- doc << "" << props.get_name() << '>' << std::endl;
+ case dt_string:
+ doc << r.get(i);
+ break;
+ case dt_double:
+ doc << r.get(i);
+ break;
+ case dt_integer:
+ doc << r.get(i);
+ break;
+ case dt_long_long:
+ doc << r.get(i);
+ break;
+ case dt_unsigned_long_long:
+ doc << r.get(i);
+ break;
+ case dt_date:
+ std::tm when = r.get(i);
+ doc << asctime(&when);
+ break;
}
- doc << "
";
+
+ doc << "" << props.get_name() << '>' << std::endl;
+}
+doc << "
";
+```
The type `T` parameter that should be passed to `row::get()` depends on the SOCI data type that is returned from `column_properties::get_data_type()`.
@@ -101,22 +103,26 @@ See the [backend documentation](backends/index.html) for details.
The `row` also provides access to indicators for each column:
- row r;
- sql << "select name from some_table where id = 1", into(r);
- if (r.get_indicator(0) != soci::i_null)
- {
- std::cout << r.get(0);
- }
+```cpp
+row r;
+sql << "select name from some_table where id = 1", into(r);
+if (r.get_indicator(0) != soci::i_null)
+{
+ std::cout << r.get(0);
+}
+```
It is also possible to extract data from the `row` object using its stream-like interface, where each extracted variable should have matching type respective to its position in the chain:
- row r;
- sql << "select name, address, age from persons where id = 123", into(r);
+```cpp
+row r;
+sql << "select name, address, age from persons where id = 123", into(r);
- string name, address;
- int age;
+string name, address;
+int age;
- r >> name >> address >> age;
+r >> name >> address >> age;
+```
Note, however, that this interface is *not* compatible with the standard `std::istream` class and that it is only possible to extract a single row at a time - for "safety" reasons the row boundary is preserved and it is necessary to perform the `fetch` operation explicitly for each consecutive row.
@@ -144,51 +150,55 @@ Note that no database-specific code is required to define user conversion.
The following example shows how the user can extend SOCI to support his own type `MyInt`, which here is some wrapper for the fundamental `int` type:
- class MyInt
+```cpp
+class MyInt
+{
+public:
+ MyInt() {}
+ MyInt(int i) : i_(i) {}
+
+ void set(int i) { i_ = i; }
+ int get() const { return i_; }
+
+private:
+ int i_;
+};
+
+namespace soci
+{
+ template <<
+ struct type_conversion
{
- public:
- MyInt() {}
- MyInt(int i) : i_(i) {}
+ typedef int base_type;
- void set(int i) { i_ = i; }
- int get() const { return i_; }
-
- private:
- int i_;
- };
-
- namespace soci
- {
- template <<
- struct type_conversion
+ static void from_base(int i, indicator ind, MyInt & mi)
{
- typedef int base_type;
-
- static void from_base(int i, indicator ind, MyInt & mi)
+ if (ind == i_null)
{
- if (ind == i_null)
- {
- throw soci_error("Null value not allowed for this type");
- }
-
- mi.set(i);
+ throw soci_error("Null value not allowed for this type");
}
- static void to_base(const MyInt & mi, int & i, indicator & ind)
- {
- i = mi.get();
- ind = i_ok;
- }
- };
- }
+ mi.set(i);
+ }
+
+ static void to_base(const MyInt & mi, int & i, indicator & ind)
+ {
+ i = mi.get();
+ ind = i_ok;
+ }
+ };
+}
+```
The above specialization for `soci::type_conversion` is enough to enable the following:
- MyInt i;
+```cpp
+MyInt i;
- sql << "select count(*) from person", into(i);
+sql << "select count(*) from person", into(i);
- cout << "We have " << i.get() << " persons in the database.\n";
+cout << "We have " << i.get() << " persons in the database.\n";
+```
Note that there is a number of types from the Boost library integrated with SOCI out of the box, see [Integration with Boost](boost.html) for complete description. Use these as examples of conversions for more complext data types.
@@ -206,73 +216,77 @@ For example, the following code maps a `Person` object to and from a database ta
Note that the mapping is non-invasive - the `Person` object itself does not contain any SOCI-specific code:
- struct Person
- {
- int id;
- std::string firstName;
- std::string lastName;
- std::string gender;
- };
+```cpp
+struct Person
+{
+ int id;
+ std::string firstName;
+ std::string lastName;
+ std::string gender;
+};
- namespace soci
+namespace soci
+{
+ template<>
+ struct type_conversion
{
- template<>
- struct type_conversion
+ typedef values base_type;
+
+ static void from_base(values const & v, indicator /* ind */, Person & p)
{
- typedef values base_type;
+ p.id = v.get("ID");
+ p.firstName = v.get("FIRST_NAME");
+ p.lastName = v.get("LAST_NAME");
- static void from_base(values const & v, indicator /* ind */, Person & p)
- {
- p.id = v.get("ID");
- p.firstName = v.get("FIRST_NAME");
- p.lastName = v.get("LAST_NAME");
+ // p.gender will be set to the default value "unknown"
+ // when the column is null:
+ p.gender = v.get("GENDER", "unknown");
- // p.gender will be set to the default value "unknown"
- // when the column is null:
- p.gender = v.get("GENDER", "unknown");
+ // alternatively, the indicator can be tested directly:
+ // if (v.indicator("GENDER") == i_null)
+ // {
+ // p.gender = "unknown";
+ // }
+ // else
+ // {
+ // p.gender = v.get("GENDER");
+ // }
+ }
- // alternatively, the indicator can be tested directly:
- // if (v.indicator("GENDER") == i_null)
- // {
- // p.gender = "unknown";
- // }
- // else
- // {
- // p.gender = v.get("GENDER");
- // }
- }
-
- static void to_base(const Person & p, values & v, indicator & ind)
- {
- v.set("ID", p.id);
- v.set("FIRST_NAME", p.firstName);
- v.set("LAST_NAME", p.lastName);
- v.set("GENDER", p.gender, p.gender.empty() ? i_null : i_ok);
- ind = i_ok;
- }
- };
- }
+ static void to_base(const Person & p, values & v, indicator & ind)
+ {
+ v.set("ID", p.id);
+ v.set("FIRST_NAME", p.firstName);
+ v.set("LAST_NAME", p.lastName);
+ v.set("GENDER", p.gender, p.gender.empty() ? i_null : i_ok);
+ ind = i_ok;
+ }
+ };
+}
+```
With the above `type_conversion` specialization in place, it is possible to use `Person` directly with SOCI:
- session sql(oracle, "service=db1 user=scott password=tiger");
+```cpp
+session sql(oracle, "service=db1 user=scott password=tiger");
- Person p;
- p.id = 1;
- p.lastName = "Smith";
- p.firstName = "Pat";
- sql << "insert into person(id, first_name, last_name) "
- "values(:ID, :FIRST_NAME, :LAST_NAME)", use(p);
+Person p;
+p.id = 1;
+p.lastName = "Smith";
+p.firstName = "Pat";
+sql << "insert into person(id, first_name, last_name) "
+ "values(:ID, :FIRST_NAME, :LAST_NAME)", use(p);
- Person p1;
- sql << "select * from person", into(p1);
- assert(p1.id == 1);
- assert(p1.firstName + p.lastName == "PatSmith");
- assert(p1.gender == "unknown");
+Person p1;
+sql << "select * from person", into(p1);
+assert(p1.id == 1);
+assert(p1.firstName + p.lastName == "PatSmith");
+assert(p1.gender == "unknown");
- p.firstName = "Patricia";
- sql << "update person set first_name = :FIRST_NAME "
- "where id = :ID", use(p);
+p.firstName = "Patricia";
+sql << "update person set first_name = :FIRST_NAME "
+ "where id = :ID", use(p);
+```
-**Note:** The `values` class is currently not suited for use outside of `type_conversion`specializations.
+Note: The `values` class is currently not suited for use outside of `type_conversion`specializations.
It is specially designed to facilitate object-relational mapping when used as shown above.
diff --git a/docs/utilities.md b/docs/utilities.md
index 80cdca42..110f0e19 100644
--- a/docs/utilities.md
+++ b/docs/utilities.md
@@ -8,67 +8,87 @@ SOCI supports some basic methods to construct portable DDL queries. That is, ins
It is possible to create a new table in a single statement:
- sql.create_table("t1").column("i", soci::dt_integer).column("j", soci::dt_integer);
+```cpp
+sql.create_table("t1").column("i", soci::dt_integer).column("j", soci::dt_integer);
+```
Above, table "t1" will be created with two columns ("i", "j") of type integer.
It is also possible to build similar statements piece by piece, which is useful if the table structure is computed dynamically:
- {
- soci::ddl_type ddl = sql.create_table("t2");
- ddl.column("i", soci::dt_integer);
- ddl.column("j", soci::dt_integer);
- ddl.column("k", soci::dt_integer)("not null");
- ddl.primary_key("t2_pk", "j");
- }
+```cpp
+{
+ soci::ddl_type ddl = sql.create_table("t2");
+ ddl.column("i", soci::dt_integer);
+ ddl.column("j", soci::dt_integer);
+ ddl.column("k", soci::dt_integer)("not null");
+ ddl.primary_key("t2_pk", "j");
+}
+```
The actual statement is executed at the end of above block, when the ddl object goes out of scope. The "not null" constraint was added to the definition of column "k" explicitly and in fact any piece of SQL can be inserted this way - with the obvious caveat of having limited portability (the "not null" piece seems to be universaly portable).
Columns can be added to and dropped from already existing tables as well:
- sql.add_column("t1", "k", soci::dt_integer);
- // or with constraint:
- //sql.add_column("t1", "k", soci::dt_integer)("not null");
-
- sql.drop_column("t1", "i");
+```cpp
+sql.add_column("t1", "k", soci::dt_integer);
+// or with constraint:
+//sql.add_column("t1", "k", soci::dt_integer)("not null");
+
+sql.drop_column("t1", "i");
+```
If needed, precision and scale can be defined with additional integer arguments to functions that create columns:
- sql.add_column("t1", "s", soci::dt_string, precision);
- sql.add_column("t1", "d", soci::dt_double, precision, scale);
+```cpp
+sql.add_column("t1", "s", soci::dt_string, precision);
+sql.add_column("t1", "d", soci::dt_double, precision, scale);
+```
Tables with foreign keys to each other can be also created:
- {
- soci::ddl_type ddl = sql.create_table("t3");
- ddl.column("x", soci::dt_integer);
- ddl.column("y", soci::dt_integer);
- ddl.foreign_key("t3_fk", "x", "t2", "j");
- }
+```cpp
+{
+ soci::ddl_type ddl = sql.create_table("t3");
+ ddl.column("x", soci::dt_integer);
+ ddl.column("y", soci::dt_integer);
+ ddl.foreign_key("t3_fk", "x", "t2", "j");
+}
+```
Tables can be dropped, too:
- sql.drop_table("t1");
- sql.drop_table("t3");
- sql.drop_table("t2");
+```cpp
+sql.drop_table("t1");
+sql.drop_table("t3");
+sql.drop_table("t2");
+```
Note that due to the differences in the set of types that are actually supported on the target database server, the type mappings, as well as precision and scales, might be different, even in the way that makes them impossible to portably recover with metadata queries.
In the category of portability utilities, the following functions are also available:
- sql.empty_blob()
+```cpp
+sql.empty_blob()
+```
the above call returns the string containing expression that represents an empty BLOB value in the given target backend. This expression can be used as part of a bigger SQL statement, for example:
- sql << "insert into my_table (x) values (" + sql.empty_blob() + ")";
+```cpp
+sql << "insert into my_table (x) values (" + sql.empty_blob() + ")";
+```
and:
- sql.nvl()
+```cpp
+sql.nvl()
+```
the above call returns the string containing the name of the SQL function that implements the NVL or COALESCE operation in the given target backend, for example:
- sql << "select name, " + sql.nvl() + "(phone, \'UNKNOWN\') from phone_book";
+```cpp
+sql << "select name, " + sql.nvl() + "(phone, \'UNKNOWN\') from phone_book";
+```
Note: `empty_blob` and `nvl` are implemented in Oracle, PostgreSQL and SQLite3 backends; for other backends their behaviour is as for PostgreSQL.
@@ -81,10 +101,11 @@ omitting the from clause in this case, others -- e.g. Oracle -- still require
providing some syntactically valid from clause even if it is not used. To use
this function, simply append the result of this function to the statement:
- double databasePi;
- session << ("select 4*atan(1)" + session.get_dummy_from_clause()),
- into(databasePi);
-
+```cpp
+double databasePi;
+session << ("select 4*atan(1)" + session.get_dummy_from_clause()),
+ into(databasePi);
+```
If just the name of the dummy table is needed, and not the full clause, you can
use `get_dummy_from_table()` to obtain it.
@@ -98,27 +119,33 @@ It is possible to portably query the database server to obtain basic metadata in
In order to get the list of table names in the current schema:
- std::vector names(100);
- sql.get_table_names(), into(names);
+```cpp
+std::vector names(100);
+sql.get_table_names(), into(names);
+```
alternatively:
- std::string name;
- soci::statement st = (sql.prepare_table_names(), into(name));
-
- st.execute();
- while (st.fetch())
- {
- // ...
- }
+```cpp
+std::string name;
+soci::statement st = (sql.prepare_table_names(), into(name));
+
+st.execute();
+while (st.fetch())
+{
+ // ...
+}
+```
Similarly, to get the description of all columns in the given table:
- soci::column_info ci;
- soci::statement st = (sql.prepare_column_descriptions(table_name), into(ci));
+```cpp
+soci::column_info ci;
+soci::statement st = (sql.prepare_column_descriptions(table_name), into(ci));
- st.execute();
- while (st.fetch())
- {
- // ci fields describe each column in turn
- }
+st.execute();
+while (st.fetch())
+{
+ // ci fields describe each column in turn
+}
+```
diff --git a/docs/vagrant.md b/docs/vagrant.md
index d5d208a2..b933b110 100644
--- a/docs/vagrant.md
+++ b/docs/vagrant.md
@@ -17,7 +17,7 @@ virtual environments for **hassle-free** SOCI development.
* during provision, automatically clones and builds SOCI from `master` branch.
* `db2.vm`:
* hostname: `vmdb2.local`
- * IBM DB2 Express-C 9.7 installed from http://archive.canonical.com packages.
+ * IBM DB2 Express-C 9.7 installed from [archive.canonical.com](http://archive.canonical.com) packages.
* `oracle.vm`:
* *TODO*: provision with Oracle XE
* SOCI local git repository (aka `$SOCI_HOME`) is automatically shared on host
@@ -35,63 +35,72 @@ it is provisioned with complete DB2 CLI client (libraries and headers).
You need to download "IBM Data Server Driver Package (DS Driver)" manually
and make it visible to Vagrant:
- 1. Go to http://www-01.ibm.com/support/docview.wss?uid=swg21385217
- 2. Download "IBM Data Server Driver Package (DS Driver)"
- 3. Copy the package to `${SOCI_HOME}/tmp` directory, on host machine.
+1. Go to [IBM Data Server Client Packages](http://www-01.ibm.com/support/docview.wss?uid=swg21385217).
+2. Download "IBM Data Server Driver Package (DS Driver)".
+3. Copy the package to `${SOCI_HOME}/tmp` directory, on host machine.
## Usage
Below, simple and easy development workflow with Vagrant is outlined:
* [Boot](https://docs.vagrantup.com/v2/getting-started/up.html)
-```
+
+```console
vagrant up
```
+
or boot VMs selectively:
-```
+
+```console
vagrant up {soci|db2}
```
+
First time you run it, be patient as Vagrant downloads VM box and
provisions it installing all the necessary packages.
* You can SSH into the machine
-```
+
+```console
vagrant ssh {soci|db2}
```
-* Develop
- * Run git commands can either from host or VM `soci` (once connected via SSH)
-```
+* Run git commands can either from host or VM `soci` (once connected via SSH)
+
+```console
cd /vagrant # aka $SOCI_HOME
git pull origin master
```
- * You can edit source code on both, on host or VM `soci`.
- * For example, edit in your favourite editor on host machine, then build,
- run, test and debug on guest machine from command line.
- * Alternatively, edit and build on host machine using your favourite IDE,
- then test and debug connecting to DBs on guest machines via network.
+
+* You can edit source code on both, on host or VM `soci`.
+* For example, edit in your favourite editor on host machine, then build, run, test and debug on guest machine from command line.
+* Alternatively, edit and build on host machine using your favourite IDE, then test and debug connecting to DBs on guest machines via network.
+
* Build on VM `soci`
-```
+
+```console
vagrant ssh soci
cd /vagrant # aka $SOCI_HOME
cd soci-build # aka $SOCI_BUILD
make
```
+
You can also execute the `build.h` script provided to run CMake and make
-```
+
+```console
vagrant ssh soci
cd $SOCI_BUILD
/vagrant/scripts/vagrant/build.sh
```
- * Debug, only on VM `soci` with gdb.
+
+* Debug, only on VM `soci`, for example, with gdb or remotely Visual Studio 2017.
* [Teardown](https://docs.vagrantup.com/v2/getting-started/teardown.html)
-```
+
+```console
vagrant {suspend|halt|destroy} {soci|db2}
```
-Check Vagrant [command-line interface](https://docs.vagrantup.com/v2/cli/index.html)
-for complete list of commands.
+Check Vagrant [command-line interface](https://docs.vagrantup.com/v2/cli/index.html) for complete list of commands.
### Environment variables
@@ -112,7 +121,5 @@ Note, those variables are also used by provision scripts to set up databases.
## Troubleshooting
* Analyze `vagrant up` output.
-* On Windows, prefer `vagrant ssh` from inside MinGW Shell where `ssh.exe` is available or
- learn how to use Vagrant with PuTTY.
-* If you modify any of `scripts/vagrant/*.sh` scripts, **ensure** they have unified
- end-of-line characters to `LF` only. Otherwise, provisioning steps may fail.
+* On Windows, prefer `vagrant ssh` from inside MinGW Shell where `ssh.exe` is available or learn how to use Vagrant with PuTTY.
+* If you modify any of `scripts/vagrant/*.sh` scripts, **ensure** they have unified end-of-line characters to `LF` only. Otherwise, provisioning steps may fail.