Commit 3c3fec62 authored by Maciej Sobczak's avatar Maciej Sobczak

Portable metadata queries.

parent cd26169e
//
// Copyright (C) 2016 Maciej Sobczak
// 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)
//
#ifndef SOCI_COLUMN_INFO_H_INCLUDED
#define SOCI_COLUMN_INFO_H_INCLUDED
#include "soci/soci-backend.h"
#include "soci/type-conversion.h"
#include "soci/values.h"
namespace soci
{
struct SOCI_DECL column_info
{
std::string name;
data_type type;
std::size_t length; // meaningful for text columns only
std::size_t precision;
std::size_t scale;
bool nullable;
};
std::size_t get_numeric_value(const values & v,
const std::string & field_name)
{
data_type dt = v.get_properties(field_name).get_data_type();
switch (dt)
{
case dt_double:
return static_cast<std::size_t>(
v.get<double>(field_name, 0.0));
case dt_integer:
return static_cast<std::size_t>(
v.get<int>(field_name, 0));
case dt_long_long:
return static_cast<std::size_t>(
v.get<long long>(field_name, 0ll));
case dt_unsigned_long_long:
return static_cast<std::size_t>(
v.get<unsigned long long>(field_name, 0ull));
break;
default:
return 0u;
}
}
template <>
struct type_conversion<column_info>
{
typedef values base_type;
static void from_base(values const & v, indicator /* ind */, column_info & ci)
{
ci.name = v.get<std::string>("COLUMN_NAME");
ci.length = get_numeric_value(v, "CHARACTER_MAXIMUM_LENGTH");
ci.precision = get_numeric_value(v, "NUMERIC_PRECISION");
ci.scale = get_numeric_value(v, "NUMERIC_SCALE");
const std::string & type_name = v.get<std::string>("DATA_TYPE");
if (type_name == "text" ||
type_name.find("char") != std::string::npos ||
type_name.find("CHAR") != std::string::npos)
{
ci.type = dt_string;
}
else if (type_name == "integer" || type_name == "INTEGER")
{
ci.type = dt_integer;
}
else if (type_name.find("number") != std::string::npos ||
type_name.find("NUMBER") != std::string::npos ||
type_name.find("numeric") != std::string::npos ||
type_name.find("NUMERIC") != std::string::npos)
{
if (ci.scale != 0)
{
ci.type = dt_double;
}
else
{
ci.type = dt_integer;
}
}
else if (type_name.find("time") != std::string::npos ||
type_name.find("TIME") != std::string::npos ||
type_name.find("date") != std::string::npos ||
type_name.find("DATE") != std::string::npos)
{
ci.type = dt_date;
}
else if (type_name.find("blob") != std::string::npos ||
type_name.find("BLOB") != std::string::npos ||
type_name.find("bytea") != std::string::npos ||
type_name.find("BYTEA") != std::string::npos)
{
ci.type = dt_blob;
}
else
{
// this seems to be a safe default
ci.type = dt_string;
}
const std::string & nullable_s = v.get<std::string>("IS_NULLABLE");
ci.nullable = (nullable_s == "YES");
}
};
} // namespace soci
#endif // SOCI_COLUMN_INFO_H_INCLUDED
......@@ -260,6 +260,24 @@ struct oracle_session_backend : details::session_backend
virtual void commit();
virtual void rollback();
virtual std::string get_table_names_query() const
{
return "select table_name"
" from user_tables";
}
virtual std::string get_column_descriptions_query() const
{
return "select column_name,"
" data_type,"
" char_length as character_maximum_length,"
" data_precision as numeric_precision,"
" data_scale as numeric_scale,"
" decode(nullable, 'Y', 'YES', 'N', 'NO') as is_nullable"
" from user_tab_columns"
" where table_name = :t";
}
virtual std::string get_backend_name() const { return "oracle"; }
void clean_up();
......
......@@ -113,7 +113,34 @@ public:
// return the last value auto-generated in this session).
bool get_last_insert_id(std::string const & table, long & value);
// Returns once_temp_type for the internally composed query
// for the list of tables in the current schema.
// Since this query usually returns multiple results (for multiple tables),
// it makes sense to bind std::vector<std::string> for the single output field.
details::once_temp_type get_table_names();
// Returns prepare_temp_type for the internally composed query
// for the list of tables in the current schema.
// Since this is intended for use with statement objects, where results are obtained one row after another,
// it makes sense to bind std::string for the output field.
details::prepare_temp_type prepare_table_names();
// Returns once_temp_type for the internally composed query
// for the list of column descriptions.
// Since this query usually returns multiple results (for multiple columns),
// it makes sense to bind std::vector<std::string> for each output field.
// Note: table_name is a non-const reference to prevent temporary objects,
// this argument is bound as a regular "use" element.
details::once_temp_type get_column_descriptions(std::string & table_name);
// Returns prepare_temp_type for the internally composed query
// for the list of column descriptions.
// Since this is intended for use with statement objects, where results are obtained one row after another,
// it makes sense to bind either std::string for each output field or soci::column_info for the whole row.
// Note: table_name is a non-const reference to prevent temporary objects,
// this argument is bound as a regular "use" element.
details::prepare_temp_type prepare_column_descriptions(std::string & table_name);
// for diagnostics and advanced users
// (downcast it to expected back-end session class)
details::session_backend * get_backend() { return backEnd_; }
......
......@@ -234,6 +234,28 @@ public:
return false;
}
// There is a set of standard SQL metadata structures that can be
// queried in a portable way - backends that are standard compliant
// do not need to override the following methods, which are intended
// to return a proper query for basic metadata statements.
virtual std::string get_table_names_query() const
{
return "select table_name as \"TABLE_NAME\""
" from information_schema.tables"
" where table_schema = 'public'";
}
virtual std::string get_column_descriptions_query() const
{
return "select column_name as \"COLUMN_NAME\","
" data_type as \"DATA_TYPE\","
" character_maximum_length as \"CHARACTER_MAXIMUM_LENGTH\","
" numeric_precision as \"NUMERIC_PRECISION\","
" numeric_scale as \"NUMERIC_SCALE\","
" is_nullable as \"IS_NULLABLE\""
" from information_schema.columns"
" where table_schema = 'public' and table_name = :t";
}
virtual std::string get_backend_name() const = 0;
virtual statement_backend* make_statement_backend() = 0;
......
......@@ -13,6 +13,7 @@
#include "soci/backend-loader.h"
#include "soci/blob.h"
#include "soci/blob-exchange.h"
#include "soci/column-info.h"
#include "soci/connection-pool.h"
#include "soci/error.h"
#include "soci/exchange-traits.h"
......
......@@ -359,6 +359,26 @@ bool session::get_last_insert_id(std::string const & sequence, long & value)
return backEnd_->get_last_insert_id(*this, sequence, value);
}
details::once_temp_type session::get_table_names()
{
return once << backEnd_->get_table_names_query();
}
details::prepare_temp_type session::prepare_table_names()
{
return prepare << backEnd_->get_table_names_query();
}
details::once_temp_type session::get_column_descriptions(std::string & table_name)
{
return once << backEnd_->get_column_descriptions_query(), use(table_name, "t");
}
details::prepare_temp_type session::prepare_column_descriptions(std::string & table_name)
{
return prepare << backEnd_->get_column_descriptions_query(), use(table_name, "t");
}
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