diff --git a/docs/api/backend.md b/docs/api/backend.md index aa1066164..372ceca9b 100644 --- a/docs/api/backend.md +++ b/docs/api/backend.md @@ -21,22 +21,25 @@ All names are defined in either `soci` or `soci::details` namespace. // data types, as seen by the user enum data_type { - dt_string, - dt_int8, - dt_uint8, - dt_int16, - dt_uint16, - dt_int32, - dt_integer = dt_int32, - dt_uint32, - dt_int64, - dt_long_long = dt_int64, - dt_uint64, - dt_unsigned_long_long = dt_uint64, - dt_double, - dt_date, - dt_blob, - dt_xml + dt_string, dt_date, dt_double, dt_integer, dt_long_long, dt_unsigned_long_long +}; + +// data types, as seen by the user +enum db_type +{ + db_string, + db_int8, + db_uint8, + db_int16, + db_uint16, + db_int32, + db_uint32, + db_int64, + db_uint64, + db_double, + db_date, + db_blob, + db_xml }; // the enum type for indicator variables @@ -86,8 +89,9 @@ public: }; ``` -The `data_type` enumeration type defines all types that form the core type support for SOCI. +The `data_type` and `db_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. +`data_type` is deprecated in favor of `db_type`, so users are encouraged to use the latter. The `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 @@ -217,6 +221,7 @@ public: virtual int prepare_for_describe() = 0; virtual void describe_column(int colNum, data_type& dtype, + db_type& dbtype, std::string& column_name) = 0; virtual standard_into_type_backend* make_into_type_backend() = 0; diff --git a/docs/api/client.md b/docs/api/client.md index febfdf9e2..386497ec5 100644 --- a/docs/api/client.md +++ b/docs/api/client.md @@ -13,7 +13,10 @@ The following types are commonly used in the rest of the interface: ```cpp // data types, as seen by the user -enum data_type { dt_string, dt_date, dt_double, dt_int8, dt_uint8, dt_int16, dt_uint16, dt_int32, dt_uint32, dt_int64, dt_uint64 }; +enum data_type { dt_string, dt_date, dt_double, dt_integer, dt_long_long, dt_unsigned_long_long }; + +// data types, as seen by the user +enum db_type { db_string, db_date, db_double, db_int8, db_uint8, db_int16, db_uint16, db_int32, db_uint32, db_int64, db_uint64 }; // the enum type for indicator variables enum indicator { i_ok, i_null, i_truncated }; @@ -22,7 +25,8 @@ enum indicator { i_ok, i_null, i_truncated }; 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 `data_type` and `db_type` types define the basic SOCI data types. User provided data types need to be associated with one of these basic types. +`data_type` is deprecated in favor of `db_type`, so users are encouraged to use the latter. The `indicator` type defines the possible states of data. @@ -443,14 +447,16 @@ class column_properties { public: std::string get_name() const; - data_type get_data_type() const; + data_type_type get_data_type() const; + db_type get_db_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. +* `get_data_type` that returns the type of the column (deprecate in favor of `get_db_type`). +* `get_db_type` that returns the type of the column. See [Dynamic resultset binding](../types.md#dynamic-binding) for examples. diff --git a/docs/backends/firebird.md b/docs/backends/firebird.md index ddb302463..c4a61d79e 100644 --- a/docs/backends/firebird.md +++ b/docs/backends/firebird.md @@ -73,17 +73,26 @@ type is not known at compile time. When calling `row::get()`, 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` specializations | +| Firebird Data Type | SOCI Data Type (`data_type`) | `row::get` specializations | | --------------------------------------- | --------------------------------------- | --------------------------------- | | numeric, decimal (where scale > 0) | dt_double | double | -| numeric, decimal [^1] (where scale = 0) | dt_int16/dt_int32/dt_int64, dt_double | dt_int16/int32_t/dt_int64, double | +| numeric, decimal [^1] (where scale = 0) | dt_integer, dt_double | int, double | | double precision, float | dt_double | double | -| smallint | dt_int16 | int16_t | -| integer | dt_int32 | int32_t | -| bigint | dt_int64 | int64_t | +| smallint, integer | dt_integer | int | | char, varchar | dt_string | std::string | | date, time, timestamp | dt_date | std::tm | +| Firebird Data Type | SOCI Data Type (`db_type`) | `row::get` specializations | +| --------------------------------------- | --------------------------------------- | --------------------------------- | +| numeric, decimal (where scale > 0) | db_double | double | +| numeric, decimal [^1] (where scale = 0) | db_int16/db_int32/db_int64, db_double | int16_t/int32_t/int64_t, double | +| double precision, float | db_double | double | +| smallint | db_int16 | int16_t | +| integer | db_int32 | int32_t | +| bigint | db_int64 | int64_t | +| char, varchar | db_string | std::string | +| date, time, timestamp | db_date | std::tm | + [^1] There is also 64bit integer type for larger values which is currently not supported. diff --git a/docs/backends/mysql.md b/docs/backends/mysql.md index c89de9be2..bbac99763 100644 --- a/docs/backends/mysql.md +++ b/docs/backends/mysql.md @@ -72,21 +72,31 @@ The MySQL backend supports the use of the SOCI `row` class, which facilitates re When calling `row::get()`, 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` specializations | -| ----------------------------------------------------------------------------------------------------------------- | --------------------- | ----------------------------- | -| FLOAT, DOUBLE, DECIMAL and synonyms | dt_double | double | -| TINYINT | dt_int8 | int8_t | -| TINYINT UNSIGNED | dt_uint8 | uint8_t | -| SMALLINT | dt_int16 | int16_t | -| SMALLINT UNSIGNED | dt_uint16 | uint16_t | -| MEDIUMINT | dt_int32 | int32_t | -| MEDIUMINT UNSIGNED | dt_uint32 | uint32_t | -| INT | dt_int32 | int32_t | -| INT UNSIGNED | dt_uint32 | uint32_t | -| BIGINT | dt_int64 | int64_t | -| BIGINT UNSIGNED | dt_uint64 | uint64_t | -| 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::tm | +| MySQL Data Type | SOCI Data Type (`data_type`) | `row::get` 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::tm | + +| MySQL Data Type | SOCI Data Type (`db_type`) | `row::get` specializations | +| ----------------------------------------------------------------------------------------------------------------- | ---------------------------- | ----------------------------- | +| FLOAT, DOUBLE, DECIMAL and synonyms | db_double | double | +| TINYINT | db_int8 | int8_t | +| TINYINT UNSIGNED | db_uint8 | uint8_t | +| SMALLINT | db_int16 | int16_t | +| SMALLINT UNSIGNED | db_uint16 | uint16_t | +| MEDIUMINT | db_int32 | int32_t | +| MEDIUMINT UNSIGNED | db_uint32 | uint32_t | +| INT | db_int32 | int32_t | +| INT UNSIGNED | db_uint32 | uint32_t | +| BIGINT | db_int64 | int64_t | +| BIGINT UNSIGNED | db_uint64 | uint64_t | +| CHAR, VARCHAR, BINARY, VARBINARY, TINYBLOB, MEDIUMBLOB, BLOB,LONGBLOB, TINYTEXT, MEDIUMTEXT, TEXT, LONGTEXT, ENUM | db_string | std::string | +| TIMESTAMP (works only with MySQL >= 5.0), DATE, TIME, DATETIME | db_date | std::tm | (See the [dynamic resultset binding](../types.md#dynamic-binding) documentation for general information on using the `Row` class.) diff --git a/docs/backends/odbc.md b/docs/backends/odbc.md index 13f9e46df..9365c0281 100644 --- a/docs/backends/odbc.md +++ b/docs/backends/odbc.md @@ -60,18 +60,25 @@ The ODBC backend supports the use of the SOCI `row` class, which facilitates ret When calling `row::get()`, 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` specializations | -| --------------------------------------------------------- | -------------- | ----------------------------- | -| SQL_DOUBLE, SQL_DECIMAL, SQL_REAL, SQL_FLOAT, SQL_NUMERIC | dt_double | double | -| SQL_TINYINT | dt_int8 | int8_t | -| SQL_SMALLINT | dt_int16 | int16_t | -| SQL_INTEGER | dt_int32 | int32_t | -| SQL_BIGINT | dt_int64 | int64_t | -| SQL_CHAR, SQL_VARCHAR | dt_string | std::string | -| SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP | dt_date | std::tm | +| ODBC Data Type | SOCI Data Type (`data_type`) | `row::get` 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::tm | + +| ODBC Data Type | SOCI Data Type (`db_type`) | `row::get` specializations | +| --------------------------------------------------------- | -------------------------- | ----------------------------- | +| SQL_DOUBLE, SQL_DECIMAL, SQL_REAL, SQL_FLOAT, SQL_NUMERIC | db_double | double | +| SQL_TINYINT | db_int8 | int8_t | +| SQL_SMALLINT | db_int16 | int16_t | +| SQL_INTEGER | db_int32 | int32_t | +| SQL_BIGINT | db_int64 | int64_t | +| SQL_CHAR, SQL_VARCHAR | db_string | std::string | +| SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP | db_date | std::tm | Not all ODBC drivers support all datatypes. -Columns having the attribute `unsigned` get mapped to their corresponding `dt_uint[n]` and `uint[n]_t` types. +Columns having the attribute `unsigned` get mapped to their corresponding `db_uint[n]` and `uint[n]_t` types. (See the [dynamic resultset binding](../types.md#dynamic-binding) documentation for general information on using the `row` class.) diff --git a/docs/backends/oracle.md b/docs/backends/oracle.md index 7eeec47c6..13317a6b9 100644 --- a/docs/backends/oracle.md +++ b/docs/backends/oracle.md @@ -71,13 +71,21 @@ The Oracle backend supports the use of the SOCI `row` class, which facilitates r When calling `row::get()`, 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` specializations | -| --------------------------------------------------------------------------------- | -------------- | ----------------------------- | -| number (where scale > 0) | dt_double | double | -| number (where scale = 0 and precision ≤ `std::numeric_limits::digits10`) | dt_int32 | int32_t | -| number (where scale = 0) | dt_int64 | int64_t | -| char, varchar, varchar2 | dt_string | std::string | -| date | dt_date | std::tm | +| Oracle Data Type | SOCI Data Type (`data_type`) | `row::get` specializations | +| --------------------------------------------------------------------------------- | ---------------------------- | ----------------------------- | +| number (where scale > 0) | dt_double | double | +| number (where scale = 0 and precision ≤ `std::numeric_limits::digits10`) | dt_integer | int | +| number (where scale = 0) | dt_long_long | long long | +| char, varchar, varchar2 | dt_string | std::string | +| date | dt_date | std::tm | + +| Oracle Data Type | SOCI Data Type (`db_type`) | `row::get` specializations | +| --------------------------------------------------------------------------------- | ---------------------------- | ----------------------------- | +| number (where scale > 0) | db_double | double | +| number (where scale = 0 and precision ≤ `std::numeric_limits::digits10`) | db_int32 | int32_t | +| number (where scale = 0) | db_int64 | int64_t | +| char, varchar, varchar2 | db_string | std::string | +| date | db_date | std::tm | (See the [dynamic resultset binding](../types.md#dynamic-binding) documentation for general information on using the `row` class.) diff --git a/docs/backends/postgresql.md b/docs/backends/postgresql.md index ce3519138..f7268ac32 100644 --- a/docs/backends/postgresql.md +++ b/docs/backends/postgresql.md @@ -88,16 +88,25 @@ The PostgreSQL backend supports the use of the SOCI `row` class, which facilitat When calling `row::get()`, 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` specializations | -| ------------------------------------------------------------ | -------------- | ----------------------------- | -| numeric, real, double | dt_double | double | -| boolean | dt_int8 | int8_t | -| smallint | dt_int16 | int16_t | -| integer | dt_int32 | int32_t | -| int8 | dt_int64 | int64_t | -| oid | dt_int32 | int32_t | -| char, varchar, text, cstring, bpchar | dt_string | std::string | -| abstime, reltime, date, time, timestamp, timestamptz, timetz | dt_date | std::tm | +| PostgreSQL Data Type | SOCI Data Type (`data_type`) | `row::get` 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::tm | + +| PostgreSQL Data Type | SOCI Data Type (`db_type`) | `row::get` specializations | +| ------------------------------------------------------------ | ---------------------------- | ----------------------------- | +| numeric, real, double | db_double | double | +| boolean | db_int8 | int8_t | +| smallint | db_int16 | int16_t | +| integer | db_int32 | int32_t | +| int8 | db_int64 | int64_t | +| oid | db_int32 | int32_t | +| char, varchar, text, cstring, bpchar | db_string | std::string | +| abstime, reltime, date, time, timestamp, timestamptz, timetz | db_date | std::tm | (See the [dynamic resultset binding](../types.md#dynamic-binding) documentation for general information on using the `row` class.) diff --git a/docs/backends/sqlite3.md b/docs/backends/sqlite3.md index a7df2ea44..a77a7f2a5 100644 --- a/docs/backends/sqlite3.md +++ b/docs/backends/sqlite3.md @@ -75,16 +75,25 @@ When calling `row::get()`, the type you should pass as T depends upon the und For the SQLite3 backend, this type mapping is complicated by the fact the SQLite3 does not enforce [types][INTEGER_PRIMARY_KEY] 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: -| SQLite3 Data Type | SOCI Data Type | `row::get` specializations | -| ------------------------------------------------------------------------------------------------------------ | -------------- | ----------------------------- | -| *float*, *decimal*, *double*, *double precision*, *number*, *numeric*, *real* | dt_double | double | -| *tinyint* | dt_int8 | int8_t | -| *smallint* | dt_int16 | int16_t | -| *int*, *integer*, *int2*, *mediumint*, *boolean* | dt_int32 | int32_t | -| *int8*, *bigint* | dt_int64 | int64_t | -| *unsigned big int* | dt_uint64 | uint64_t | -| *text*, *char*, *character*, *clob*, *native character*, *nchar*, *nvarchar*, *varchar*, *varying character* | dt_string | std::string | -| *date*, *time*, *datetime* | dt_date | std::tm | +| SQLite3 Data Type | SOCI Data Type (`data_type`) | `row::get` specializations | +| ------------------------------------------------------------------------------------------------------------ | ---------------------------- | ----------------------------- | +| *float*, *decimal*, *double*, *double precision*, *number*, *numeric*, *real* | dt_double | double | +| *int*, *integer*, *int2*, *mediumint*, *boolean* | dt_integer | int | +| *int8*, *bigint* | dt_long_long | long long | +| *unsigned big int* | dt_unsigned_long_long | unsigned long long | +| *text*, *char*, *character*, *clob*, *native character*, *nchar*, *nvarchar*, *varchar*, *varying character* | dt_string | std::string | +| *date*, *time*, *datetime* | dt_date | std::tm | + +| SQLite3 Data Type | SOCI Data Type (`db_type`) | `row::get` specializations | +| ------------------------------------------------------------------------------------------------------------ | ---------------------------- | ----------------------------- | +| *float*, *decimal*, *double*, *double precision*, *number*, *numeric*, *real* | db_double | double | +| *tinyint* | db_int8 | int8_t | +| *smallint* | db_int16 | int16_t | +| *int*, *integer*, *int2*, *mediumint*, *boolean* | db_int32 | int32_t | +| *int8*, *bigint* | db_int64 | int64_t | +| *unsigned big int* | db_uint64 | uint64_t | +| *text*, *char*, *character*, *clob*, *native character*, *nchar*, *nvarchar*, *varchar*, *varying character* | db_string | std::string | +| *date*, *time*, *datetime* | db_date | std::tm | [INTEGER_PRIMARY_KEY] : 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. diff --git a/docs/types.md b/docs/types.md index e571049d5..e71962611 100644 --- a/docs/types.md +++ b/docs/types.md @@ -64,39 +64,39 @@ for(std::size_t i = 0; i != r.size(); ++i) doc << '<' << props.get_name() << '>'; - switch(props.get_data_type()) + switch(props.get_db_type()) { - case dt_string: + case db_string: doc << r.get(i); break; - case dt_double: + case db_double: doc << r.get(i); break; - case dt_int8: + case db_int8: doc << r.get(i); break; - case dt_uint8: + case db_uint8: doc << r.get(i); break; - case dt_int16: + case db_int16: doc << r.get(i); break; - case dt_uint16: + case db_uint16: doc << r.get(i); break; - case dt_int32: + case db_int32: doc << r.get(i); break; - case dt_uint32: + case db_uint32: doc << r.get(i); break; - case dt_int64: + case db_int64: doc << r.get(i); break; - case dt_uint64: + case db_uint64: doc << r.get(i); break; - case dt_date: + case db_date: std::tm when = r.get(i); doc << asctime(&when); break; @@ -107,26 +107,33 @@ for(std::size_t i = 0; i != r.size(); ++i) 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()`. +The type `T` parameter that should be passed to `row::get()` depends on the SOCI data type that is returned from `data_type column_properties::get_data_type()` or `db_type column_properties::get_db_type()`. +Users are encouraged to use the latter as it supports a wider range of numerical C++ types. `row::get()` throws an exception of type `std::bad_cast` if an incorrect type `T` is requested. -| SOCI Data Type | `row::get` specialization | -|-------------------------|------------------------------| -| `dt_double` | `double` | -| `dt_int8` | `int8_t` | -| `dt_uint8` | `uint8_t` | -| `dt_int16` | `int16_t` | -| `dt_uint16` | `uint16_t` | -| `dt_int32` | `int32_t` | -| `dt_integer` | `int32_t` | -| `dt_uint32` | `uint32_t` | -| `dt_int64` | `int64_t` | -| `dt_long_long` | `int64_t` | -| `dt_uint64` | `uint64_t` | -| `dt_unsigned_long_long` | `uint64_t` | -| `dt_string` | `std::string` | -| `dt_date` | `std::tm` | +| SOCI Data Type (`data_type`) | `row::get` 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::tm` | + +| SOCI Data Type (`db_type`) | `row::get` specialization | +|------------------------------|------------------------------| +| `db_double` | `double` | +| `db_int8` | `int8_t` | +| `db_uint8` | `uint8_t` | +| `db_int16` | `int16_t` | +| `db_uint16` | `uint16_t` | +| `db_int32` | `int32_t` | +| `db_uint32` | `uint32_t` | +| `db_int64` | `int64_t` | +| `db_uint64` | `uint64_t` | +| `db_string` | `std::string` | +| `db_date` | `std::tm` | The mapping of underlying database column types to SOCI datatypes is database specific. See the [backend documentation](backends/index.md) for details. diff --git a/docs/utilities.md b/docs/utilities.md index 110f0e19b..4a331dbc0 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -9,7 +9,7 @@ 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: ```cpp -sql.create_table("t1").column("i", soci::dt_integer).column("j", soci::dt_integer); +sql.create_table("t1").column("i", soci::db_int32).column("j", soci::db_int32); ``` Above, table "t1" will be created with two columns ("i", "j") of type integer. @@ -19,9 +19,9 @@ It is also possible to build similar statements piece by piece, which is useful ```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.column("i", soci::db_int32); + ddl.column("j", soci::db_int32); + ddl.column("k", soci::db_int32)("not null"); ddl.primary_key("t2_pk", "j"); } ``` @@ -31,9 +31,9 @@ The actual statement is executed at the end of above block, when the ddl object Columns can be added to and dropped from already existing tables as well: ```cpp -sql.add_column("t1", "k", soci::dt_integer); +sql.add_column("t1", "k", soci::db_int32); // or with constraint: -//sql.add_column("t1", "k", soci::dt_integer)("not null"); +//sql.add_column("t1", "k", soci::db_int32)("not null"); sql.drop_column("t1", "i"); ``` @@ -41,8 +41,8 @@ sql.drop_column("t1", "i"); If needed, precision and scale can be defined with additional integer arguments to functions that create columns: ```cpp -sql.add_column("t1", "s", soci::dt_string, precision); -sql.add_column("t1", "d", soci::dt_double, precision, scale); +sql.add_column("t1", "s", soci::db_string, precision); +sql.add_column("t1", "d", soci::db_double, precision, scale); ``` Tables with foreign keys to each other can be also created: @@ -50,8 +50,8 @@ Tables with foreign keys to each other can be also created: ```cpp { soci::ddl_type ddl = sql.create_table("t3"); - ddl.column("x", soci::dt_integer); - ddl.column("y", soci::dt_integer); + ddl.column("x", soci::db_int32); + ddl.column("y", soci::db_int32); ddl.foreign_key("t3_fk", "x", "t2", "j"); } ```