Commit 8d2a75ba authored by Maciej Sobczak's avatar Maciej Sobczak

Added support for portable DDL statements.

parent 4244fae0
......@@ -5,6 +5,7 @@
* [Bulk operations](#bulk)
* [Stored procedures](#procedures)
* [Transactions](#transactions)
* [Portable DDL](#ddl)
* [Metadata queries](#metadata)
* [Basic logging support](#logging)
......@@ -290,6 +291,58 @@ A typical usage pattern for this class might be:
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.
### <a name="ddl"></a> Portable DDL
SOCI supports some basic methods to construct portable DDL queries. That is, instead of writing explicit SQL statement for creating or modifying tables, it is possible to use dedicated SOCI functions, which prepare appropriate DDL statements behind the scenes, thus enabling the user application to create basic database structures in a way that is portable across different database servers. Note that the actual support for these functions depends on the actual backend implementation.
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);
Above, table "t1" will be created with two columns ("i", "j") of type integer.
It is also possible to build such a statement piece by piece:
{
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");
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);
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");
}
Tables can be dropped, too:
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.
### <a name="metadata"></a> Metadata queries
It is possible to portably query the database server to obtain basic metadata information.
......
//
// Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
// Copyright (C) 2004-2016 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -109,6 +109,48 @@ private:
} // namespace details
// Note: ddl_type is intended to be used just as once_type_type,
// but since it can be also used directly (explicitly) by the user code,
// it is declared outside of the namespace details.
class SOCI_DECL ddl_type
{
public:
ddl_type(session & s);
ddl_type(const ddl_type & d);
ddl_type & operator=(const ddl_type & d);
~ddl_type() SOCI_NOEXCEPT_FALSE;
void create_table(const std::string & tableName);
void add_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale);
void alter_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale);
void drop_column(const std::string & tableName,
const std::string & columnName);
ddl_type & column(const std::string & columnName, data_type dt,
int precision = 0, int scale = 0);
ddl_type & unique(const std::string & name,
const std::string & columnNames);
ddl_type & primary_key(const std::string & name,
const std::string & columnNames);
ddl_type & foreign_key(const std::string & name,
const std::string & columnNames,
const std::string & refTableName,
const std::string & refColumnNames);
ddl_type & operator()(const std::string & arbitrarySql);
void set_tail(const std::string & tail);
private:
session * s_;
details::ref_counted_statement * rcst_;
};
} // namespace soci
#endif
//
// Copyright (C) 2004-2007 Maciej Sobczak, Stephen Hutton
// Copyright (C) 2004-2016 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -25,6 +25,7 @@
#include <soci/soci-backend.h>
#include <oci.h> // OCI
#include <sstream>
#include <vector>
#ifdef _MSC_VER
......@@ -278,6 +279,77 @@ struct oracle_session_backend : details::session_backend
" where table_name = :t";
}
virtual std::string create_column(const std::string & columnName, data_type dt,
int precision, int scale)
{
// Oracle-specific SQL syntax:
std::string res = columnName + " ";
switch (dt)
{
case dt_string:
{
std::ostringstream oss;
oss << "varchar(" << precision << ")";
res += oss.str();
}
break;
case dt_date:
res += "timestamp";
break;
case dt_double:
{
std::ostringstream oss;
if (precision == 0)
{
oss << "number";
}
else
{
oss << "number(" << precision << ", " << scale << ")";
}
res += oss.str();
}
break;
case dt_integer:
res += "integer";
break;
case dt_long_long:
res += "number";
break;
case dt_unsigned_long_long:
res += "number";
break;
case dt_blob:
res += "blob";
break;
}
return res;
}
virtual std::string add_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
return "alter table " + tableName + " add " +
create_column(columnName, dt, precision, scale);
}
virtual std::string alter_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
return "alter table " + tableName + " modify " +
create_column(columnName, dt, precision, scale);
}
virtual std::string get_backend_name() const { return "oracle"; }
void clean_up();
......
//
// Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
// Copyright (C) 2004-2016 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -37,6 +37,11 @@ public:
{
try
{
if (tail_.empty() == false)
{
accumulate(tail_);
}
final_action();
}
catch (...)
......@@ -52,6 +57,10 @@ public:
template <typename T>
void accumulate(T const & t) { get_query_stream() << t; }
void set_tail(const std::string & tail) { tail_ = tail; }
void set_need_comma(bool need_comma) { need_comma_ = need_comma; }
bool get_need_comma() const { return need_comma_; }
protected:
// this function allows to break the circular dependenc
// between session and this class
......@@ -61,6 +70,10 @@ protected:
session & session_;
// used mainly for portable ddl
std::string tail_;
bool need_comma_;
private:
SOCI_NOT_COPYABLE(ref_counted_statement_base)
};
......
//
// Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
// Copyright (C) 2004-2016 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -83,7 +83,7 @@ public:
cxx_details::auto_ptr<details::query_transformation_function> qtf(new details::query_transformation<T>(callback));
set_query_transformation_(qtf);
}
}
// support for basic logging
void set_log_stream(std::ostream * s);
......@@ -133,6 +133,20 @@ public:
// this argument is bound as a regular "use" element.
details::prepare_temp_type prepare_column_descriptions(std::string & table_name);
// Functions for basic portable DDL statements.
ddl_type create_table(const std::string & tableName);
void drop_table(const std::string & tableName);
void truncate_table(const std::string & tableName);
ddl_type add_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision = 0, int scale = 0);
ddl_type alter_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision = 0, int scale = 0);
ddl_type drop_column(const std::string & tableName,
const std::string & columnName);
// for diagnostics and advanced users
// (downcast it to expected back-end session class)
details::session_backend * get_backend() { return backEnd_; }
......
//
// Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
// Copyright (C) 2004-2016 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -14,6 +14,7 @@
#include <cstddef>
#include <map>
#include <string>
#include <sstream>
namespace soci
{
......@@ -260,7 +261,117 @@ public:
" from information_schema.columns"
" where table_schema = 'public' and table_name = :t";
}
virtual std::string create_table(const std::string & tableName)
{
return "create table " + tableName + " (";
}
virtual std::string drop_table(const std::string & tableName)
{
return "drop table " + tableName;
}
virtual std::string truncate_table(const std::string & tableName)
{
return "truncate table " + tableName;
}
virtual std::string create_column(const std::string & columnName, data_type dt,
int precision, int scale)
{
// PostgreSQL was selected as a baseline for the syntax:
std::string res = columnName + " ";
switch (dt)
{
case dt_string:
{
std::ostringstream oss;
oss << "varchar(" << precision << ")";
res += oss.str();
}
break;
case dt_date:
res += "timestamp";
break;
case dt_double:
{
std::ostringstream oss;
if (precision == 0)
{
oss << "numeric";
}
else
{
oss << "numeric(" << precision << ", " << scale << ")";
}
res += oss.str();
}
break;
case dt_integer:
res += "integer";
break;
case dt_long_long:
res += "bigint";
break;
case dt_unsigned_long_long:
res += "bigint";
break;
case dt_blob:
res += "bytea";
break;
}
return res;
}
virtual std::string add_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
return "alter table " + tableName + " add column " +
create_column(columnName, dt, precision, scale);
}
virtual std::string alter_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
return "alter table " + tableName + " alter column " +
create_column(columnName, dt, precision, scale);
}
virtual std::string drop_column(const std::string & tableName,
const std::string & columnName)
{
return "alter table " + tableName +
" drop column " + columnName;
}
virtual std::string constraint_unique(const std::string & name,
const std::string & columnNames)
{
return "constraint " + name +
" unique (" + columnNames + ")";
}
virtual std::string constraint_primary_key(const std::string & name,
const std::string & columnNames)
{
return "constraint " + name +
" primary key (" + columnNames + ")";
}
virtual std::string constraint_foreign_key(const std::string & name,
const std::string & columnNames,
const std::string & refTableName,
const std::string & refColumnNames)
{
return "constraint " + name +
" foreign key (" + columnNames + ")" +
" references " + refTableName + " (" + refColumnNames + ")";
}
virtual std::string get_backend_name() const = 0;
virtual statement_backend* make_statement_backend() = 0;
......
//
// Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
// Copyright (C) 2004-2016 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -45,3 +45,136 @@ once_temp_type & once_temp_type::operator,(into_type_ptr const & i)
rcst_->exchange(i);
return *this;
}
ddl_type::ddl_type(session & s)
: s_(&s), rcst_(new ref_counted_statement(s))
{
// this is the beginning of new query
s.get_query_stream().str("");
}
ddl_type::ddl_type(const ddl_type & d)
:rcst_(d.rcst_)
{
rcst_->inc_ref();
}
ddl_type & ddl_type::operator=(const ddl_type & d)
{
d.rcst_->inc_ref();
rcst_->dec_ref();
rcst_ = d.rcst_;
return *this;
}
ddl_type::~ddl_type() SOCI_NOEXCEPT_FALSE
{
rcst_->dec_ref();
}
void ddl_type::create_table(const std::string & tableName)
{
rcst_->accumulate(s_->get_backend()->create_table(tableName));
}
void ddl_type::add_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
rcst_->accumulate(s_->get_backend()->add_column(
tableName, columnName, dt, precision, scale));
}
void ddl_type::alter_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
rcst_->accumulate(s_->get_backend()->alter_column(
tableName, columnName, dt, precision, scale));
}
void ddl_type::drop_column(const std::string & tableName,
const std::string & columnName)
{
rcst_->accumulate(s_->get_backend()->drop_column(
tableName, columnName));
}
ddl_type & ddl_type::column(const std::string & columnName, data_type dt,
int precision, int scale)
{
if (rcst_->get_need_comma())
{
rcst_->accumulate(", ");
}
rcst_->accumulate(s_->get_backend()->create_column(
columnName, dt, precision, scale));
rcst_->set_need_comma(true);
return *this;
}
ddl_type & ddl_type::unique(const std::string & name,
const std::string & columnNames)
{
if (rcst_->get_need_comma())
{
rcst_->accumulate(", ");
}
rcst_->accumulate(s_->get_backend()->constraint_unique(
name, columnNames));
rcst_->set_need_comma(true);
return *this;
}
ddl_type & ddl_type::primary_key(const std::string & name,
const std::string & columnNames)
{
if (rcst_->get_need_comma())
{
rcst_->accumulate(", ");
}
rcst_->accumulate(s_->get_backend()->constraint_primary_key(
name, columnNames));
rcst_->set_need_comma(true);
return *this;
}
ddl_type & ddl_type::foreign_key(const std::string & name,
const std::string & columnNames,
const std::string & refTableName,
const std::string & refColumnNames)
{
if (rcst_->get_need_comma())
{
rcst_->accumulate(", ");
}
rcst_->accumulate(s_->get_backend()->constraint_foreign_key(
name, columnNames, refTableName, refColumnNames));
rcst_->set_need_comma(true);
return *this;
}
ddl_type & ddl_type::operator()(const std::string & arbitrarySql)
{
rcst_->accumulate(" " + arbitrarySql);
return *this;
}
void ddl_type::set_tail(const std::string & tail)
{
rcst_->set_tail(tail);
}
//
// Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton
// Copyright (C) 2004-2016 Maciej Sobczak, Stephen Hutton
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
......@@ -13,8 +13,7 @@ using namespace soci;
using namespace soci::details;
ref_counted_statement_base::ref_counted_statement_base(session& s)
: refCount_(1)
, session_(s)
: refCount_(1), session_(s), need_comma_(false)
{
}
......
......@@ -374,6 +374,58 @@ details::prepare_temp_type session::prepare_column_descriptions(std::string & ta
return prepare << backEnd_->get_column_descriptions_query(), use(table_name, "t");
}
ddl_type session::create_table(const std::string & tableName)
{
ddl_type ddl(*this);
ddl.create_table(tableName);
ddl.set_tail(")");
return ddl;
}
void session::drop_table(const std::string & tableName)
{
once << backEnd_->drop_table(tableName);
}
void session::truncate_table(const std::string & tableName)
{
once << backEnd_->truncate_table(tableName);
}
ddl_type session::add_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
ddl_type ddl(*this);
ddl.add_column(tableName, columnName, dt, precision, scale);
return ddl;
}
ddl_type session::alter_column(const std::string & tableName,
const std::string & columnName, data_type dt,
int precision, int scale)
{
ddl_type ddl(*this);
ddl.alter_column(tableName, columnName, dt, precision, scale);
return ddl;
}
ddl_type session::drop_column(const std::string & tableName,
const std::string & columnName)
{
ddl_type ddl(*this);
ddl.drop_column(tableName, columnName);
return ddl;
}
std::string session::get_backend_name() const
{
ensureConnected(backEnd_);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment