In fact there is a problem with as_lower_d semantic action. I've patched the sql_parser.cpp to make it work. Make sure you choose the right SQLPP_AS_LOWER_D macro (see beginning of the code):
#include <boost/spirit.hpp>
#include <sqlpp/config.hpp>
#include <sqlpp/utils.hpp>
#include <sqlpp/field.hpp>
#include <sqlpp/table.hpp>
#include <sqlpp/properties/database_property.hpp>
#include <sqlpp/database.hpp>
#include <sqlpp/unique_constraint.hpp>
#include <sqlpp/ref_constraint.hpp>
#include <sqlpp/parsers/field_list_grammar.hpp>
#define SQLPP_AS_LOWER_D( s, S )\
( str_p( #s ) | str_p( #S ) )
namespace sqlpp{
namespace parsers{
namespace details{
enum Constraint
{
PrimaryKey,
Unique,
ForeignKey,
Check
};
struct match
{
match()
: full_match(false){};
void clear()
{
name.clear();
full_match=false;
}
string_type name;
bool full_match;
};
struct field_match : match
{
field_match() : not_null(false){};
string_type type;
bool not_null;
};
struct constraint_match : match
{
string_vector_type fields;
void clear()
{
match::clear();
fields.clear();
}
};
struct ref_constraint_match : constraint_match
{
ref_constraint_match()
:
on_delete(ref_constraint::EventActionUnknown),
on_update(ref_constraint::EventActionUnknown),
match_type(ref_constraint::MatchUnknown)
{};
string_type ref_table;
ref_constraint::EventAction on_delete;
ref_constraint::EventAction on_update;
ref_constraint::Match match_type;
};
struct check_constraint_match : constraint_match
{
string_type predicate;
};
class table_match
{
public:
typedef std::vector<field_match> field_match_container_type;
typedef std::pair<
field_match_container_type::const_iterator,
field_match_container_type::const_iterator
> field_match_const_iterator_range;
typedef std::vector<constraint_match> constraint_container_type;
typedef std::pair<
constraint_container_type::const_iterator,
constraint_container_type::const_iterator
> constraint_const_iterator_range;
typedef std::vector<ref_constraint_match> ref_constraint_container_type;
typedef std::pair<
ref_constraint_container_type::const_iterator,
ref_constraint_container_type::const_iterator
> ref_constraint_const_iterator_range;
typedef std::vector<check_constraint_match> check_constraint_container_type;
typedef std::pair<
check_constraint_container_type::const_iterator,
check_constraint_container_type::const_iterator
> check_constraint_const_iterator_range;
table_match()
: m_fields(1), m_uniques(1), m_refs(1), m_checks(1)
{};
void clear()
{
name.clear();
m_primary_key.clear();
m_fields.clear(); m_fields.push_back(field_match());
m_uniques.clear(); m_uniques.push_back(constraint_match());
m_refs.clear(); m_refs.push_back(ref_constraint_match());
m_checks.clear(); m_checks.push_back(check_constraint_match());
}
field_match& field()
{
assert(!m_fields.empty());
return m_fields.back();
}
void add_field()
{
field().full_match=true;
m_fields.push_back(field_match());
}
field_match_const_iterator_range fields() const
{
return field_match_const_iterator_range(
m_fields.begin(),
m_fields.end()
);};
constraint_match& primary_key()
{
return m_primary_key;
};
void add_primary_key()
{
assert(!m_primary_key.full_match);
m_primary_key.full_match = true;
};
void assign_fields_to_primary_key()
{
assert(!m_primary_key.full_match);
assert(m_primary_key.fields.empty());
m_primary_key.fields = field_names;
field_names.clear();
}
constraint_match& unique()
{
assert(!m_uniques.empty());
return m_uniques.back();
}
void add_unique()
{
unique().full_match=true;
m_uniques.push_back(constraint_match());
}
void assign_fields_to_unique()
{
assert(!(unique().full_match));
assert(unique().fields.empty());
unique().fields = field_names;
field_names.clear();
}
constraint_const_iterator_range uniques() const
{
return constraint_const_iterator_range(
m_uniques.begin(),
m_uniques.end()
);
};
ref_constraint_match& ref()
{
assert(!m_refs.empty());
return m_refs.back();
}
void add_ref()
{
ref().full_match=true;
m_refs.push_back(ref_constraint_match());
}
void assign_fields_to_ref()
{
assert(!(ref().full_match));
assert(ref().fields.empty());
ref().fields = field_names;
field_names.clear();
}
ref_constraint_const_iterator_range refs() const
{
return ref_constraint_const_iterator_range(
m_refs.begin(),
m_refs.end()
);
};
check_constraint_match& check()
{
assert(!m_checks.empty());
return m_checks.back();
}
void add_check()
{
check().full_match=true;
m_checks.push_back(check_constraint_match());
}
void assign_fields_to_check()
{
assert(!(check().full_match));
assert(check().fields.empty());
check().fields = field_names;
field_names.clear();
}
check_constraint_const_iterator_range checks() const
{
return check_constraint_const_iterator_range(
m_checks.begin(),
m_checks.end()
);
};
string_type name;
string_vector_type field_names;
private:
field_match_container_type m_fields;
constraint_match m_primary_key;
constraint_container_type m_uniques;
ref_constraint_container_type m_refs;
check_constraint_container_type m_checks;
};
struct is_name_equal
{
is_name_equal(string_const_reference name_)
: m_name(name_){};
template< typename Value >
bool operator () (Value const& v) const
{
return v.name==m_name;
};
string_type m_name;
};
template< typename T>
struct assign_name
{
assign_name( T& t_)
:
m_t(t_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
m_t.name.assign(first,last);
}
T& m_t;
};
struct add_field
{
table_match& m_table;
add_field( table_match& table_)
:
m_table(table_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
m_table.add_field();
}
};
struct add_primary_key
{
table_match& m_table;
bool m_inline;
add_primary_key( table_match& table_, bool inline_)
:
m_table(table_), m_inline(inline_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
if (m_inline)
m_table.primary_key().fields.push_back( m_table.field().name );
m_table.add_primary_key();
}
};
struct add_unique
{
table_match& m_table;
bool m_inline;
add_unique( table_match& table_, bool inline_)
:
m_table(table_), m_inline(inline_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
if (m_inline)
m_table.unique().fields.push_back( m_table.field().name );
m_table.add_unique();
}
};
struct add_ref
{
table_match& m_table;
bool m_inline;
add_ref( table_match& table_, bool inline_)
:
m_table(table_), m_inline(inline_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
if (m_inline)
m_table.ref().fields.push_back( m_table.field().name );
m_table.add_ref();
}
};
struct add_check
{
table_match& m_table;
bool m_inline;
add_check( table_match& table_, bool inline_)
:
m_table(table_), m_inline(inline_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
if (m_inline)
m_table.check().fields.push_back( m_table.field().name );
m_table.add_check();
}
};
struct assign_field
{
enum Value
{
Name,
Type,
NotNull
};
table_match& m_table;
Value m_value;
assign_field( table_match& table_, Value value_)
:
m_table(table_),
m_value(value_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
string_type s(first,last);
switch(m_value)
{
case Name:
m_table.field().name=s;break;
case Type:
m_table.field().type=s; break;
case NotNull:
m_table.field().not_null=true; break;
};
};
};
struct assign_primary_key
{
enum Value
{
Name
};
table_match& m_table;
Value m_value;
assign_primary_key( table_match& table_, Value value_)
:
m_table(table_),
m_value(value_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
string_type s(first,last);
switch(m_value)
{
case Name:
m_table.primary_key().name=s;break;
default:
assert(false);
};
};
};
struct assign_unique
{
enum Value
{
Name
};
table_match& m_table;
Value m_value;
assign_unique( table_match& table_, Value value_)
:
m_table(table_),
m_value(value_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
string_type s(first,last);
switch(m_value)
{
case Name:
m_table.unique().name=s;break;
default:
assert(false);
};
};
};
struct assign_ref
{
enum Value
{
Name,
OnDeleteCascade,
OnDeleteSetNull,
OnUpdateCascade,
OnUpdateSetNull,
MatchFull,
MatchPartial,
ReferenceTable
};
table_match& m_table;
Value m_value;
assign_ref( table_match& table_, Value value_)
:
m_table(table_),
m_value(value_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
string_type s(first,last);
switch(m_value)
{
case Name:
m_table.ref().name =s; break;
case OnDeleteCascade:
m_table.ref().on_delete=ref_constraint::EventActionCascade; break;
case OnDeleteSetNull:
m_table.ref().on_delete=ref_constraint::EventActionSetNull; break;
case OnUpdateCascade:
m_table.ref().on_update=ref_constraint::EventActionCascade; break;
case OnUpdateSetNull:
m_table.ref().on_update=ref_constraint::EventActionSetNull; break;
case MatchFull:
m_table.ref().match_type = ref_constraint::MatchFull; break;
case MatchPartial:
m_table.ref().match_type = ref_constraint::MatchPartial; break;
case ReferenceTable:
m_table.ref().ref_table=s; break;
};
};
};
struct assign_fields
{
table_match& m_table;
Constraint m_type;
assign_fields( table_match& table_, Constraint type_)
:
m_table(table_),
m_type(type_)
{};
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
switch(m_type)
{
case PrimaryKey:
m_table.assign_fields_to_primary_key(); break;
case Unique:
m_table.assign_fields_to_unique(); break;
case ForeignKey:
m_table.assign_fields_to_ref(); break;
case Check:
m_table.assign_fields_to_check(); break;
default:
assert(false);
}
}
};
struct add_table
{
add_table( database_shared_ptr db_, table_match& table_)
:
m_db(db_),
m_table(table_)
{};
table_shared_ptr create_table( string_const_reference name_) const
{
if (!m_db->contains_table(name_))
return m_db->add_table( name_ );
else
return m_db->get_table( name_ );
};
void set_primary_key( table_shared_ptr table_, constraint_match const& pk_) const
{
if (pk_.full_match)
table_->set_primary_key( pk_.fields, pk_.name);
};
bool add_unique( table_shared_ptr table_, constraint_match const& u_) const
{
assert(u_.full_match);
return table_->add_unique( u_.fields, u_.name );
};
bool add_ref( table_shared_ptr table_, ref_constraint_match const& fk_) const
{
assert(fk_.full_match);
return table_->add_ref(
fk_.fields,
create_table( fk_.ref_table),
fk_.name,
fk_.on_delete,
fk_.on_update,
fk_.match_type
);
};
void add_check( table_shared_ptr table_, check_constraint_match const& c_) const
{
assert(table_);
};
void add_field( table_shared_ptr table_, field_match const& fm_) const
{
assert(table_);
table_->add_field( fm_.name, fm_.type, fm_.not_null );
}
template <typename IteratorT>
void operator()(IteratorT first, IteratorT last) const
{
table_shared_ptr table = create_table(m_table.name);
assert(table);
table_match::field_match_container_type::const_iterator itf, itf_end;
for (
boost::tie(itf, itf_end) = m_table.fields();
itf != itf_end;
++itf)
{
if (!itf->full_match)
continue;
add_field( table,*itf );
};
if(m_table.primary_key().full_match)
set_primary_key(table,m_table.primary_key());
table_match::ref_constraint_container_type::const_iterator itr,itr_end;
for (
boost::tie(itr, itr_end) = m_table.refs();
itr != itr_end;
++itr)
{
if (!itr->full_match)
continue;
add_ref( table,*itr );
};
table_match::constraint_container_type::const_iterator itu,itu_end;
for (
boost::tie(itu, itu_end) = m_table.uniques();
itu != itu_end;
++itu)
{
if (!itu->full_match)
continue;
add_unique( table,*itu );
};
table_match::check_constraint_container_type::const_iterator itc,itc_end;
for (
boost::tie(itc, itc_end) = m_table.checks();
itc != itc_end;
++itc)
{
if (!itc->full_match)
continue;
add_check( table,*itc );
};
m_table.clear();
}
database_shared_ptr m_db;
table_match& m_table;
};
struct type_grammar :
boost::spirit::grammar<type_grammar>
{
template <typename ScannerT>
struct definition
{
boost::spirit::rule<ScannerT>
bracketed_int,
bracketed_pair_int,
smallint_type,
int_type,
numeric_type,
decimal_type,
float_type,
char_type,
varchar_type,
bit_type,
date_type,
time_type,
timestamp_type,
interval_type,
types;
definition(type_grammar const& self_)
{
using namespace boost::spirit;
bracketed_int = confix_p( ch_p('('), int_p, ch_p(')'));
bracketed_pair_int = confix_p( ch_p('('), int_p >> "," >> int_p, ch_p(')'));
smallint_type = SQLPP_AS_LOWER_D(smallint, SMALLINT);
int_type = lexeme_d[ SQLPP_AS_LOWER_D(int,INT) >> !( SQLPP_AS_LOWER_D(eger,EGER) ) ];
numeric_type= SQLPP_AS_LOWER_D(numeric,NUMERIC) >> bracketed_pair_int;
decimal_type = SQLPP_AS_LOWER_D(decimal,DECIMAL) >> bracketed_pair_int;
float_type= SQLPP_AS_LOWER_D(float,FLOAT) >> bracketed_int;
char_type= ( SQLPP_AS_LOWER_D(char,CHAR) | SQLPP_AS_LOWER_D(character,CHARACTER) )
>> bracketed_int;
varchar_type = (
(SQLPP_AS_LOWER_D(character,CHARACTER)
>> SQLPP_AS_LOWER_D(varying,VARYING)) |
SQLPP_AS_LOWER_D(varchar,VARCHAR)
)
>> bracketed_int;
bit_type = (SQLPP_AS_LOWER_D(bit,BIT) >> ! SQLPP_AS_LOWER_D(varying,VARYING)) >> bracketed_int;
date_type = SQLPP_AS_LOWER_D(date,DATE);
time_type = SQLPP_AS_LOWER_D(time,TIME);
timestamp_type = SQLPP_AS_LOWER_D(timestamp,TIMESTAMP);
interval_type = SQLPP_AS_LOWER_D(interval,INTERVAL);
types =
smallint_type
| int_type
| numeric_type
| decimal_type
| float_type
| char_type
| varchar_type
| bit_type
| date_type
| time_type
| timestamp_type
| interval_type
;
BOOST_SPIRIT_DEBUG_NODE(bracketed_int);
BOOST_SPIRIT_DEBUG_NODE(bracketed_pair_int);
BOOST_SPIRIT_DEBUG_NODE(smallint_type);
BOOST_SPIRIT_DEBUG_NODE(int_type);
BOOST_SPIRIT_DEBUG_NODE(numeric_type);
BOOST_SPIRIT_DEBUG_NODE(decimal_type);
BOOST_SPIRIT_DEBUG_NODE(float_type);
BOOST_SPIRIT_DEBUG_NODE(char_type);
BOOST_SPIRIT_DEBUG_NODE(varchar_type);
BOOST_SPIRIT_DEBUG_NODE(bit_type);
BOOST_SPIRIT_DEBUG_NODE(date_type);
BOOST_SPIRIT_DEBUG_NODE(time_type);
BOOST_SPIRIT_DEBUG_NODE(timestamp_type);
BOOST_SPIRIT_DEBUG_NODE(interval_type);
BOOST_SPIRIT_DEBUG_NODE(types);
};
boost::spirit::rule<ScannerT> const& start() const { return types; }
};
};
class create_table_grammar :
public boost::spirit::grammar<create_table_grammar>,
public properties::database_property
{
private:
table_match& m_table_match;
public:
create_table_grammar(
database_shared_ptr database_,
table_match& table_match_
)
:
properties::database_property(database_),
m_table_match(table_match_)
{};
table_match& get_table_match() const { return m_table_match;};
template <typename ScannerT>
struct definition
{
type_grammar types;
name_grammar name;
field_list_grammar field_list;
boost::spirit::rule<ScannerT>
constraint_name,
primary_key,
primary_key_name,
on,
delete_,
update,
cascade,
set_null,
on_delete_update,
foreign_key,
foreign_key_name,
unique,
unique_name,
not_null,
field,
fields,
create_table,
table,
r;
definition(create_table_grammar const& self_)
: field_list(self_.get_table_match().field_names)
{
using namespace boost::spirit;
constraint_name = SQLPP_AS_LOWER_D(constraint,CONSTRAINT) >> name;
primary_key = SQLPP_AS_LOWER_D(primary,PRIMARY) >> SQLPP_AS_LOWER_D(key,KEY);
primary_key_name =
(!constraint_name)
>> primary_key
>> field_list[assign_fields(self_.get_table_match(), PrimaryKey)];
on=SQLPP_AS_LOWER_D(on,ON);
cascade=SQLPP_AS_LOWER_D(cascade,CASCADE);
set_null=SQLPP_AS_LOWER_D(set,SET)>>SQLPP_AS_LOWER_D(null,NULL);
delete_ = SQLPP_AS_LOWER_D(delete,DELETE) >> (
cascade[assign_ref(self_.get_table_match(), assign_ref::OnDeleteCascade)]
| set_null[assign_ref(self_.get_table_match(), assign_ref::OnDeleteSetNull )]);
update = SQLPP_AS_LOWER_D(update,UPDATE) >> (
cascade[assign_ref(self_.get_table_match(), assign_ref::OnDeleteCascade)]
| set_null[assign_ref(self_.get_table_match(), assign_ref::OnDeleteSetNull )]);
on_delete_update = on >> ( delete_ >> !update | update >> !delete_);
foreign_key =
SQLPP_AS_LOWER_D(references,REFERENCES)
>> name[assign_ref(self_.get_table_match(), assign_ref::ReferenceTable )]
>> *(on_delete_update);
foreign_key_name =
!constraint_name >> SQLPP_AS_LOWER_D(foreign,FOREIGN) >> SQLPP_AS_LOWER_D(key,KEY)
>> field_list[ assign_fields(self_.get_table_match(), ForeignKey ) ]
>> foreign_key;
unique = SQLPP_AS_LOWER_D(unique,UNIQUE);
unique_name =
! constraint_name
>> unique
>> field_list[assign_fields(self_.get_table_match(), Unique)];
not_null=
SQLPP_AS_LOWER_D(not,NOT)>>SQLPP_AS_LOWER_D(null,NULL);
field =
name[assign_field(self_.get_table_match(), assign_field::Name ) ]
>> types[assign_field(self_.get_table_match(), assign_field::Type ) ]
>> *(
not_null[assign_field(self_.get_table_match(), assign_field::NotNull ) ]
| primary_key[ add_primary_key(self_.get_table_match(),true)]
| foreign_key[ add_ref(self_.get_table_match(),true)]
| unique[ add_unique(self_.get_table_match(),true)]
);
create_table =
SQLPP_AS_LOWER_D(create,CREATE)
>> SQLPP_AS_LOWER_D(table,TABLE)
>> name
[
assign_name<table_match>( self_.get_table_match() )
];
fields = list_p( field[add_field(self_.get_table_match())], ch_p(',') )
>> !(
ch_p(',')
>> list_p(
primary_key_name[ add_primary_key(self_.get_table_match(),false)]
| foreign_key_name[ add_ref(self_.get_table_match(),false)]
| unique_name[ add_unique(self_.get_table_match(),false)]
, ch_p(',')
)
);
table =
create_table
>> ch_p('(')
>> fields
>> ch_p(')');
r = *table
[
add_table(
self_.get_checked_database(),
self_.get_table_match()
)
];
BOOST_SPIRIT_DEBUG_NODE(constraint_name);
BOOST_SPIRIT_DEBUG_NODE(field_list);
BOOST_SPIRIT_DEBUG_NODE(primary_key);
BOOST_SPIRIT_DEBUG_NODE(primary_key_name);
BOOST_SPIRIT_DEBUG_NODE(on);
BOOST_SPIRIT_DEBUG_NODE(delete_);
BOOST_SPIRIT_DEBUG_NODE(update);
BOOST_SPIRIT_DEBUG_NODE(cascade);
BOOST_SPIRIT_DEBUG_NODE(set_null);
BOOST_SPIRIT_DEBUG_NODE(on_delete_update);
BOOST_SPIRIT_DEBUG_NODE(foreign_key);
BOOST_SPIRIT_DEBUG_NODE(foreign_key_name);
BOOST_SPIRIT_DEBUG_NODE(unique);
BOOST_SPIRIT_DEBUG_NODE(unique_name);
BOOST_SPIRIT_DEBUG_NODE(not_null);
BOOST_SPIRIT_DEBUG_NODE(field);
BOOST_SPIRIT_DEBUG_NODE(fields);
BOOST_SPIRIT_DEBUG_NODE(create_table);
BOOST_SPIRIT_DEBUG_NODE(table);
BOOST_SPIRIT_DEBUG_NODE(name);
BOOST_SPIRIT_DEBUG_NODE(types);
BOOST_SPIRIT_DEBUG_NODE(r);
};
boost::spirit::rule<ScannerT> const& start() const { return r; }
};
};
};
bool parse_sql(
database_shared_ptr db_,
string_const_reference sql_
)
{
details::table_match tb;
details::create_table_grammar tg( db_, tb);
return boost::spirit::parse(
sql_.begin(),
sql_.end(),
tg,
boost::spirit::space_p
).hit;
};
};
};
Jonathan de Halleux.
|