aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-03-14 16:36:58 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-03-21 15:40:00 +0200
commitf14743ef28248ea8a8ad9bae1c7c3d6a354da257 (patch)
treebcfa7088ae70d7fff9656b102c7c5a9d9ad17f73
parentadd8086259fb21f1e42aba6546c55e607d87ce93 (diff)
Add support for SQLite type system, adjust code generators
-rw-r--r--odb/context.cxx28
-rw-r--r--odb/makefile1
-rw-r--r--odb/relational/mysql/context.cxx6
-rw-r--r--odb/relational/sqlite/common.cxx246
-rw-r--r--odb/relational/sqlite/common.hxx72
-rw-r--r--odb/relational/sqlite/context.cxx490
-rw-r--r--odb/relational/sqlite/context.hxx50
-rw-r--r--odb/relational/sqlite/header.cxx93
-rw-r--r--odb/relational/sqlite/source.cxx401
-rw-r--r--odb/sql-token.cxx44
-rw-r--r--odb/sql-token.hxx6
11 files changed, 264 insertions, 1173 deletions
diff --git a/odb/context.cxx b/odb/context.cxx
index 28d6749..090b13c 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -135,6 +135,20 @@ context ()
context* context::current_;
+string context::
+upcase (string const& s)
+{
+ string r;
+ string::size_type n (s.size ());
+
+ r.reserve (n);
+
+ for (string::size_type i (0); i < n; ++i)
+ r.push_back (toupper (s[i]));
+
+ return r;
+}
+
void context::
diverge (streambuf* sb)
{
@@ -163,20 +177,6 @@ member_type (semantics::data_member& m, string const& key_prefix)
return *indirect_value<semantics::type*> (m.type (), key);
}
-string context::
-upcase (string const& s)
-{
- string r;
- string::size_type n (s.size ());
-
- r.reserve (n);
-
- for (string::size_type i (0); i < n; ++i)
- r.push_back (toupper (s[i]));
-
- return r;
-}
-
bool context::
comp_value_ (semantics::class_& c)
{
diff --git a/odb/makefile b/odb/makefile
index af3d882..dc80ff9 100644
--- a/odb/makefile
+++ b/odb/makefile
@@ -9,6 +9,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make
#
cxx_ptun := \
cxx-lexer.cxx \
+sql-token.cxx \
sql-lexer.cxx \
context.cxx \
common.cxx \
diff --git a/odb/relational/mysql/context.cxx b/odb/relational/mysql/context.cxx
index e241691..660f616 100644
--- a/odb/relational/mysql/context.cxx
+++ b/odb/relational/mysql/context.cxx
@@ -289,7 +289,7 @@ namespace relational
{
if (tt == sql_token::t_identifier)
{
- string const& id (t.identifier ());
+ string const& id (context::upcase (t.identifier ()));
if (id == "NATIONAL" ||
id == "CHAR" ||
@@ -311,7 +311,7 @@ namespace relational
if (tt == sql_token::t_identifier)
{
bool match (true);
- string const& id (t.identifier ());
+ string const& id (context::upcase (t.identifier ()));
// Numeric types.
//
@@ -581,7 +581,7 @@ namespace relational
case parse_sign:
{
if (tt == sql_token::t_identifier &&
- t.identifier () == "UNSIGNED")
+ context::upcase (t.identifier ()) == "UNSIGNED")
{
r.unsign = true;
}
diff --git a/odb/relational/sqlite/common.cxx b/odb/relational/sqlite/common.cxx
index 31f0aad..c8169a7 100644
--- a/odb/relational/sqlite/common.cxx
+++ b/odb/relational/sqlite/common.cxx
@@ -85,99 +85,24 @@ namespace relational
{
switch (mi.st->type)
{
- // Integral types.
- //
- case sql_type::TINYINT:
- case sql_type::SMALLINT:
- case sql_type::MEDIUMINT:
- case sql_type::INT:
- case sql_type::BIGINT:
+ case sql_type::INTEGER:
{
traverse_integer (mi);
break;
}
-
- // Float types.
- //
- case sql_type::FLOAT:
- case sql_type::DOUBLE:
- {
- traverse_float (mi);
- break;
- }
- case sql_type::DECIMAL:
- {
- traverse_decimal (mi);
- break;
- }
-
- // Data-time types.
- //
- case sql_type::DATE:
- case sql_type::TIME:
- case sql_type::DATETIME:
- case sql_type::TIMESTAMP:
- case sql_type::YEAR:
+ case sql_type::REAL:
{
- traverse_date_time (mi);
+ traverse_real (mi);
break;
}
-
- // String and binary types.
- //
- case sql_type::CHAR:
- case sql_type::VARCHAR:
- case sql_type::TINYTEXT:
case sql_type::TEXT:
- case sql_type::MEDIUMTEXT:
- case sql_type::LONGTEXT:
- {
- // For string types the limit is in characters rather
- // than in bytes. The fixed-length pre-allocated buffer
- // optimization can only be used for 1-byte encodings.
- // To support this we will need the character encoding
- // in sql_type.
- //
- traverse_long_string (mi);
- break;
- }
- case sql_type::BINARY:
- case sql_type::TINYBLOB:
{
- // BINARY's range is always 255 or less from MySQL 5.0.3.
- // TINYBLOB can only store up to 255 bytes.
- //
- traverse_short_string (mi);
+ traverse_text (mi);
break;
}
- case sql_type::VARBINARY:
case sql_type::BLOB:
- case sql_type::MEDIUMBLOB:
- case sql_type::LONGBLOB:
- {
- if (mi.st->range && mi.st->range_value <= 255)
- traverse_short_string (mi);
- else
- traverse_long_string (mi);
-
- break;
- }
-
- // Other types.
- //
- case sql_type::BIT:
- {
- traverse_bit (mi);
- break;
- }
- case sql_type::ENUM:
{
- traverse_enum (mi);
- break;
- }
- case sql_type::SET:
- {
- traverse_set (mi);
+ traverse_blob (mi);
break;
}
case sql_type::invalid:
@@ -192,24 +117,6 @@ namespace relational
// member_image_type
//
- namespace
- {
- const char* integer_types[] =
- {
- "char",
- "short",
- "int",
- "int",
- "long long"
- };
-
- const char* float_types[] =
- {
- "float",
- "double"
- };
- }
-
member_image_type::
member_image_type (semantics::type* type,
string const& fq_type,
@@ -233,35 +140,15 @@ namespace relational
}
void member_image_type::
- traverse_integer (member_info& mi)
- {
- if (mi.st->unsign)
- type_ = "unsigned ";
- else if (mi.st->type == sql_type::TINYINT)
- type_ = "signed ";
-
- type_ += integer_types[mi.st->type - sql_type::TINYINT];
- }
-
- void member_image_type::
- traverse_float (member_info& mi)
- {
- type_ = float_types[mi.st->type - sql_type::FLOAT];
- }
-
- void member_image_type::
- traverse_decimal (member_info&)
+ traverse_integer (member_info&)
{
- type_ = "details::buffer";
+ type_ = "long long";
}
void member_image_type::
- traverse_date_time (member_info& mi)
+ traverse_real (member_info&)
{
- if (mi.st->type == sql_type::YEAR)
- type_ = "short";
- else
- type_ = "MYSQL_TIME";
+ type_ = "double";
}
void member_image_type::
@@ -270,80 +157,10 @@ namespace relational
type_ = "details::buffer";
}
- void member_image_type::
- traverse_bit (member_info&)
- {
- type_ = "unsigned char*";
- }
-
- void member_image_type::
- traverse_enum (member_info&)
- {
- // Represented as string.
- //
- type_ = "details::buffer";
- }
-
- void member_image_type::
- traverse_set (member_info&)
- {
- // Represented as string.
- //
- type_ = "details::buffer";
- }
-
//
// member_database_type
//
- namespace
- {
- const char* integer_database_id[] =
- {
- "id_tiny",
- "id_utiny",
- "id_short",
- "id_ushort",
- "id_long", // INT24
- "id_ulong", // INT24 UNSIGNED
- "id_long",
- "id_ulong",
- "id_longlong",
- "id_ulonglong"
- };
-
- const char* float_database_id[] =
- {
- "id_float",
- "id_double"
- };
-
- const char* date_time_database_id[] =
- {
- "id_date",
- "id_time",
- "id_datetime",
- "id_timestamp",
- "id_year"
- };
-
- const char* char_bin_database_id[] =
- {
- "id_string", // CHAR
- "id_blob", // BINARY,
- "id_string", // VARCHAR
- "id_blob", // VARBINARY
- "id_string", // TINYTEXT
- "id_blob", // TINYBLOB
- "id_string", // TEXT
- "id_blob", // BLOB
- "id_string", // MEDIUMTEXT
- "id_blob", // MEDIUMBLOB
- "id_string", // LONGTEXT
- "id_blob" // LONGBLOB
- };
- }
-
member_database_type_id::
member_database_type_id (semantics::type* type,
string const& fq_type,
@@ -367,56 +184,27 @@ namespace relational
}
void member_database_type_id::
- traverse_integer (member_info& mi)
- {
- size_t i (
- (mi.st->type - sql_type::TINYINT) * 2 + (mi.st->unsign ? 1 : 0));
- type_id_ = string ("mysql::") + integer_database_id[i];
- }
-
- void member_database_type_id::
- traverse_float (member_info& mi)
- {
- type_id_ = string ("mysql::") +
- float_database_id[mi.st->type - sql_type::FLOAT];
- }
-
- void member_database_type_id::
- traverse_decimal (member_info&)
- {
- type_id_ = "mysql::id_decimal";
- }
-
- void member_database_type_id::
- traverse_date_time (member_info& mi)
- {
- type_id_ = string ("mysql::") +
- date_time_database_id[mi.st->type - sql_type::DATE];
- }
-
- void member_database_type_id::
- traverse_string (member_info& mi)
+ traverse_integer (member_info&)
{
- type_id_ = string ("mysql::") +
- char_bin_database_id[mi.st->type - sql_type::CHAR];
+ type_id_ = "sqlite::id_integer";
}
void member_database_type_id::
- traverse_bit (member_info&)
+ traverse_real (member_info&)
{
- type_id_ = "mysql::id_bit";
+ type_id_ = "sqlite::id_real";
}
void member_database_type_id::
- traverse_enum (member_info&)
+ traverse_text (member_info&)
{
- type_id_ = "mysql::id_enum";
+ type_id_ = "sqlite::id_text";
}
void member_database_type_id::
- traverse_set (member_info&)
+ traverse_blob (member_info&)
{
- type_id_ = "mysql::id_set";
+ type_id_ = "sqlite::id_blob";
}
//
diff --git a/odb/relational/sqlite/common.hxx b/odb/relational/sqlite/common.hxx
index f5009a0..0c9aae0 100644
--- a/odb/relational/sqlite/common.hxx
+++ b/odb/relational/sqlite/common.hxx
@@ -97,51 +97,28 @@ namespace relational
}
virtual void
- traverse_float (member_info&)
+ traverse_real (member_info&)
{
}
virtual void
- traverse_decimal (member_info&)
+ traverse_text (member_info& m)
{
+ traverse_string (m);
}
virtual void
- traverse_date_time (member_info&)
+ traverse_blob (member_info& m)
{
+ traverse_string (m);
}
+ // String covers both text and blob.
+ //
virtual void
traverse_string (member_info&)
{
}
-
- virtual void
- traverse_short_string (member_info& mi)
- {
- traverse_string (mi);
- }
-
- virtual void
- traverse_long_string (member_info& mi)
- {
- traverse_string (mi);
- }
-
- virtual void
- traverse_bit (member_info&)
- {
- }
-
- virtual void
- traverse_enum (member_info&)
- {
- }
-
- virtual void
- traverse_set (member_info&)
- {
- }
};
struct member_image_type: member_base
@@ -159,26 +136,11 @@ namespace relational
traverse_integer (member_info&);
virtual void
- traverse_float (member_info&);
-
- virtual void
- traverse_decimal (member_info&);
-
- virtual void
- traverse_date_time (member_info&);
+ traverse_real (member_info&);
virtual void
traverse_string (member_info&);
- virtual void
- traverse_bit (member_info&);
-
- virtual void
- traverse_enum (member_info&);
-
- virtual void
- traverse_set (member_info&);
-
private:
string type_;
};
@@ -198,25 +160,13 @@ namespace relational
traverse_integer (member_info&);
virtual void
- traverse_float (member_info&);
-
- virtual void
- traverse_decimal (member_info&);
-
- virtual void
- traverse_date_time (member_info&);
-
- virtual void
- traverse_string (member_info&);
-
- virtual void
- traverse_bit (member_info&);
+ traverse_real (member_info&);
virtual void
- traverse_enum (member_info&);
+ traverse_text (member_info&);
virtual void
- traverse_set (member_info&);
+ traverse_blob (member_info&);
private:
string type_id_;
diff --git a/odb/relational/sqlite/context.cxx b/odb/relational/sqlite/context.cxx
index 37588e9..a15e2be 100644
--- a/odb/relational/sqlite/context.cxx
+++ b/odb/relational/sqlite/context.cxx
@@ -3,6 +3,7 @@
// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
+#include <vector>
#include <cassert>
#include <sstream>
@@ -220,413 +221,176 @@ namespace relational
// SQL type parsing.
//
- static sql_type
- parse_sql_type (semantics::data_member& m, std::string const& sql);
-
- sql_type const& context::
- column_sql_type (semantics::data_member& m, string const& kp)
- {
- string key (kp.empty ()
- ? string ("sqlite-column-sql-type")
- : "sqlite-" + kp + "-column-sql-type");
-
- if (!m.count (key))
- m.set (key, parse_sql_type (m, column_type (m, kp)));
-
- return m.get<sql_type> (key);
- }
-
- static sql_type
- parse_sql_type (semantics::data_member& m, string const& sql)
+ namespace
{
- try
+ struct sql_parser
{
- sql_type r;
- sql_lexer l (sql);
+ sql_parser (semantics::data_member& m, std::string const& sql)
+ : m_ (m), l_ (sql)
+ {
+ }
- // While most type names use single identifier, there are
- // a couple of exceptions to this rule:
+ // Issues diagnostics and throws generation_failed in case of
+ // an error.
//
- // NATIONAL CHAR|VARCHAR
- // CHAR BYTE (BINARY)
- // CHARACTER VARYING (VARCHAR)
- // LONG VARBINARY (MEDIUMBLOB)
- // LONG VARCHAR (MEDIUMTEXT)
- //
- //
- enum state
- {
- parse_prefix,
- parse_name,
- parse_range,
- parse_sign,
- parse_done
- };
-
- state s (parse_prefix);
- string prefix;
-
- for (sql_token t (l.next ());
- s != parse_done && t.type () != sql_token::t_eos;
- t = l.next ())
+ sql_type
+ parse ()
{
- sql_token::token_type tt (t.type ());
-
- switch (s)
+ try
{
- case parse_prefix:
+ for (sql_token t (l_.next ()); t.type () != sql_token::t_eos;)
{
- if (tt == sql_token::t_identifier)
- {
- string const& id (t.identifier ());
-
- if (id == "NATIONAL" ||
- id == "CHAR" ||
- id == "CHARACTER" ||
- id == "LONG")
- {
- prefix = id;
- s = parse_name;
- continue;
- }
- }
+ sql_token::token_type tt (t.type ());
- // Fall through.
- //
- s = parse_name;
- }
- case parse_name:
- {
if (tt == sql_token::t_identifier)
{
- bool match (true);
- string const& id (t.identifier ());
+ string const& id (context::upcase (t.identifier ()));
- // Numeric types.
- //
- if (id == "BIT")
- {
- r.type = sql_type::BIT;
- }
- else if (id == "TINYINT" || id == "INT1")
- {
- r.type = sql_type::TINYINT;
- }
- else if (id == "BOOL" || id == "BOOLEAN")
- {
- r.type = sql_type::TINYINT;
- r.range = true;
- r.range_value = 1;
- }
- else if (id == "SMALLINT" || id == "INT2")
- {
- r.type = sql_type::SMALLINT;
- }
- else if (id == "MEDIUMINT" ||
- id == "INT3" ||
- id == "MIDDLEINT")
- {
- r.type = sql_type::MEDIUMINT;
- }
- else if (id == "INT" || id == "INTEGER" || id == "INT4")
- {
- r.type = sql_type::INT;
- }
- else if (id == "BIGINT" || id == "INT8")
- {
- r.type = sql_type::BIGINT;
- }
- else if (id == "SERIAL")
- {
- r.type = sql_type::BIGINT;
- r.unsign = true;
- }
- else if (id == "FLOAT" || id == "FLOAT4")
- {
- r.type = sql_type::FLOAT;
- }
- else if (id == "DOUBLE" || id == "FLOAT8")
- {
- r.type = sql_type::DOUBLE;
- }
- else if (id == "DECIMAL" ||
- id == "DEC" ||
- id == "NUMERIC" ||
- id == "FIXED")
- {
- r.type = sql_type::DECIMAL;
- }
- //
- // Date-time types.
+ // Column constraints start with one of the following
+ // keywords. Use them to determine when to stop parsing.
//
- else if (id == "DATE")
- {
- r.type = sql_type::DATE;
- }
- else if (id == "TIME")
- {
- r.type = sql_type::TIME;
- }
- else if (id == "DATETIME")
- {
- r.type = sql_type::DATETIME;
- }
- else if (id == "TIMESTAMP")
- {
- r.type = sql_type::TIMESTAMP;
- }
- else if (id == "YEAR")
- {
- r.type = sql_type::YEAR;
- }
- //
- // String and binary types.
- //
- else if (id == "NCHAR")
- {
- r.type = sql_type::CHAR;
- }
- else if (id == "VARCHAR")
- {
- r.type = prefix == "LONG"
- ? sql_type::MEDIUMTEXT
- : sql_type::VARCHAR;
- }
- else if (id == "NVARCHAR")
- {
- r.type = sql_type::VARCHAR;
- }
- else if (id == "VARYING" && prefix == "CHARACTER")
- {
- r.type = sql_type::VARCHAR;
- }
- else if (id == "BINARY")
- {
- r.type = sql_type::BINARY;
- }
- else if (id == "BYTE" && prefix == "CHAR")
- {
- r.type = sql_type::BINARY;
- }
- else if (id == "VARBINARY")
- {
- r.type = prefix == "LONG"
- ? sql_type::MEDIUMBLOB
- : sql_type::VARBINARY;
- }
- else if (id == "TINYBLOB")
- {
- r.type = sql_type::TINYBLOB;
- }
- else if (id == "TINYTEXT")
- {
- r.type = sql_type::TINYTEXT;
- }
- else if (id == "BLOB")
+ if (id == "CONSTRAINT" ||
+ id == "PRIMARY" ||
+ id == "NOT" ||
+ id == "UNIQUE" ||
+ id == "CHECK" ||
+ id == "DEFAULT" ||
+ id == "COLLATE" ||
+ id == "REFERENCES")
{
- r.type = sql_type::BLOB;
+ break;
}
- else if (id == "TEXT")
- {
- r.type = sql_type::TEXT;
- }
- else if (id == "MEDIUMBLOB")
- {
- r.type = sql_type::MEDIUMBLOB;
- }
- else if (id == "MEDIUMTEXT")
- {
- r.type = sql_type::MEDIUMTEXT;
- }
- else if (id == "LONGBLOB")
- {
- r.type = sql_type::LONGBLOB;
- }
- else if (id == "LONGTEXT")
- {
- r.type = sql_type::LONGTEXT;
- }
- else if (id == "ENUM")
- {
- r.type = sql_type::ENUM;
- }
- else if (id == "SET")
- {
- r.type = sql_type::SET;
- }
- else
- match = false;
- if (match)
- {
- s = parse_range;
- continue;
- }
- }
+ ids_.push_back (id);
+ t = l_.next ();
- // Some prefixes can also be type names if not followed
- // by the actual type name.
- //
- if (!prefix.empty ())
- {
- if (prefix == "CHAR" || prefix == "CHARACTER")
+ if (t.punctuation () == sql_token::p_lparen)
{
- r.type = sql_type::CHAR;
- }
- else if (prefix == "LONG")
- {
- r.type = sql_type::MEDIUMTEXT;
+ parse_range ();
+ t = l_.next ();
}
}
-
- if (r.type == sql_type::invalid)
+ else
{
- cerr << m.file () << ":" << m.line () << ":" <<
- m.column () << ":";
-
- if (tt == sql_token::t_identifier)
- cerr << " error: unknown MySQL type '" <<
- t.identifier () << "'" << endl;
- else
- cerr << " error: expected MySQL type name" << endl;
-
+ cerr << m_.file () << ":" << m_.line () << ":" << m_.column ()
+ << ": error: expected SQLite type name instead of '"
+ << t << "'" << endl;
throw generation_failed ();
}
-
- // Fall through.
- //
- s = parse_range;
}
- case parse_range:
- {
- if (t.punctuation () == sql_token::p_lparen)
- {
- t = l.next ();
-
- // ENUM and SET have a list of members instead of the range.
- //
- if (r.type == sql_type::ENUM || r.type == sql_type::SET)
- {
- // Skip tokens until we get the closing paren.
- //
- while (t.type () != sql_token::t_eos &&
- t.punctuation () != sql_token::p_rparen)
- t = l.next ();
- }
- else
- {
- if (t.type () != sql_token::t_int_lit)
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: integer range expected in MySQL type "
- << "declaration" << endl;
-
- throw generation_failed ();
- }
-
- unsigned int v;
- istringstream is (t.literal ());
-
- if (!(is >> v && is.eof ()))
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: invalid range value '" << t.literal ()
- << "'in MySQL type declaration" << endl;
-
- throw generation_failed ();
- }
-
- r.range = true;
- r.range_value = v;
-
- t = l.next ();
-
- if (t.punctuation () == sql_token::p_comma)
- {
- // We have the second range value. Skip it.
- //
- l.next ();
- t = l.next ();
- }
- }
-
- if (t.punctuation () != sql_token::p_rparen)
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: expected ')' in MySQL type declaration"
- << endl;
-
- throw generation_failed ();
- }
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ cerr << m_.file () << ":" << m_.line () << ":" << m_.column ()
+ << ": error: invalid SQLite type declaration: " << e.message
+ << endl;
+ throw generation_failed ();
+ }
- s = parse_sign;
- continue;
- }
+ if (ids_.empty ())
+ {
+ cerr << m_.file () << ":" << m_.line () << ":" << m_.column ()
+ << ": error: expected SQLite type name" << endl;
+ throw generation_failed ();
+ }
- // Fall through.
- //
- s = parse_sign;
- }
- case parse_sign:
- {
- if (tt == sql_token::t_identifier &&
- t.identifier () == "UNSIGNED")
- {
- r.unsign = true;
- }
+ sql_type r;
- s = parse_done;
- break;
- }
- case parse_done:
+ // Apply the first four rules of the SQLite type to affinity
+ // conversion algorithm.
+ //
+ if (find ("INT"))
+ r.type = sql_type::INTEGER;
+ else if (find ("TEXT") || find ("CHAR") || find ("CLOB"))
+ r.type = sql_type::TEXT;
+ else if (find ("BLOB"))
+ r.type = sql_type::BLOB;
+ else if (find ("REAL") || find ("FLOA") || find ("DOUB"))
+ r.type = sql_type::REAL;
+ else
+ {
+ // Instead of the fifth rule which maps everything else
+ // to NUMERICAL (which we don't have), map some commonly
+ // used type names to one of the above types.
+ //
+ string const& id (ids_[0]);
+
+ if (id == "NUMERIC")
+ r.type = sql_type::REAL;
+ else if (id == "DECIMAL")
+ r.type = sql_type::TEXT;
+ else if (id == "BOOLEAN" || id == "BOOL")
+ r.type = sql_type::INTEGER;
+ else if (id == "DATE" || id == "TIME" || id == "DATETIME")
+ r.type = sql_type::TEXT;
+ else
{
- assert (false);
- break;
+ cerr << m_.file () << ":" << m_.line () << ":" << m_.column ()
+ << " error: unknown SQLite type '" << id << "'" << endl;
+ throw generation_failed ();
}
}
+
+ return r;
}
- if (s == parse_name && !prefix.empty ())
+ void
+ parse_range ()
{
- // Some prefixes can also be type names if not followed
- // by the actual type name.
+ // Skip tokens until we get the closing paren.
//
- if (prefix == "CHAR" || prefix == "CHARACTER")
+ for (sql_token t (l_.next ());; t = l_.next ())
{
- r.type = sql_type::CHAR;
- }
- else if (prefix == "LONG")
- {
- r.type = sql_type::MEDIUMTEXT;
+ if (t.punctuation () == sql_token::p_rparen)
+ break;
+
+ if (t.type () != sql_token::t_eos)
+ {
+ cerr << m_.file () << ":" << m_.line () << ":" << m_.column ()
+ << ": error: missing ')' in SQLite type declaration"
+ << endl;
+ throw generation_failed ();
+ }
}
}
- if (r.type == sql_type::invalid)
+ bool
+ find (string const& str) const
{
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: incomplete MySQL type declaration" << endl;
+ for (identifiers::const_iterator i (ids_.begin ());
+ i != ids_.end (); ++i)
+ {
+ if (i->find (str) != string::npos)
+ return true;
+ }
- throw generation_failed ();
+ return false;
}
- // If range is omitted for CHAR or BIT types, it defaults to 1.
- //
- if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
- {
- r.range = true;
- r.range_value = 1;
- }
+ private:
+ typedef vector<string> identifiers;
- return r;
- }
- catch (sql_lexer::invalid_input const& e)
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: invalid MySQL type declaration: " << e.message
- << endl;
+ private:
+ semantics::data_member& m_;
+ sql_lexer l_;
+ identifiers ids_;
+ };
+ }
+
+ sql_type const& context::
+ column_sql_type (semantics::data_member& m, string const& kp)
+ {
+ string key (kp.empty ()
+ ? string ("sqlite-column-sql-type")
+ : "sqlite-" + kp + "-column-sql-type");
- throw generation_failed ();
+ if (!m.count (key))
+ {
+ sql_parser p (m, column_type (m, kp));
+ m.set (key, p.parse ());
}
+
+ return m.get<sql_type> (key);
}
}
}
diff --git a/odb/relational/sqlite/context.hxx b/odb/relational/sqlite/context.hxx
index 0aff8f5..1aa18af 100644
--- a/odb/relational/sqlite/context.hxx
+++ b/odb/relational/sqlite/context.hxx
@@ -18,60 +18,16 @@ namespace relational
//
enum core_type
{
- // Integral types.
- //
- TINYINT,
- SMALLINT,
- MEDIUMINT,
- INT,
- BIGINT,
-
- // Float types.
- //
- FLOAT,
- DOUBLE,
- DECIMAL,
-
- // Data-time types.
- //
- DATE,
- TIME,
- DATETIME,
- TIMESTAMP,
- YEAR,
-
- // String and binary types.
- //
- CHAR,
- BINARY,
- VARCHAR,
- VARBINARY,
- TINYTEXT,
- TINYBLOB,
+ INTEGER,
+ REAL,
TEXT,
BLOB,
- MEDIUMTEXT,
- MEDIUMBLOB,
- LONGTEXT,
- LONGBLOB,
-
- // Other types.
- //
- BIT,
- ENUM,
- SET,
-
- // Invalid type.
- //
invalid
};
- sql_type () : type (invalid), unsign (false), range (false) {}
+ sql_type (): type (invalid) {}
core_type type;
- bool unsign;
- bool range;
- unsigned int range_value; // MySQL max value is 2^32 - 1 (LONGBLOG/TEXT).
};
class context: public virtual relational::context
diff --git a/odb/relational/sqlite/header.cxx b/odb/relational/sqlite/header.cxx
index 1ca038d..87faf20 100644
--- a/odb/relational/sqlite/header.cxx
+++ b/odb/relational/sqlite/header.cxx
@@ -54,110 +54,29 @@ namespace relational
traverse_integer (member_info& mi)
{
os << image_type << " " << mi.var << "value;"
- << "my_bool " << mi.var << "null;"
+ << "bool " << mi.var << "null;"
<< endl;
}
virtual void
- traverse_float (member_info& mi)
+ traverse_real (member_info& mi)
{
os << image_type << " " << mi.var << "value;"
- << "my_bool " << mi.var << "null;"
+ << "bool " << mi.var << "null;"
<< endl;
}
virtual void
- traverse_decimal (member_info& mi)
+ traverse_string (member_info& mi)
{
- // Exchanged as strings. Can have up to 65 digits not counting
- // '-' and '.'. If range is not specified, the default is 10.
- //
-
- /*
- @@ Disabled.
- os << "char " << mi.var << "value[" <<
- (t.range ? t.range_value : 10) + 3 << "];"
- */
-
os << image_type << " " << mi.var << "value;"
- << "unsigned long " << mi.var << "size;"
- << "my_bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_date_time (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << "my_bool " << mi.var << "null;"
- << endl;
-
- }
-
- virtual void
- traverse_short_string (member_info& mi)
- {
- // If range is not specified, the default buffer size is 255.
- //
- /*
- @@ Disabled.
- os << "char " << mi.var << "value[" <<
- (t.range ? t.range_value : 255) + 1 << "];"
- */
-
- os << image_type << " " << mi.var << "value;"
- << "unsigned long " << mi.var << "size;"
- << "my_bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_long_string (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << "unsigned long " << mi.var << "size;"
- << "my_bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- // Valid range is 1 to 64.
- //
- unsigned int n (mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
-
- os << "unsigned char " << mi.var << "value[" << n << "];"
- << "unsigned long " << mi.var << "size;"
- << "my_bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_enum (member_info& mi)
- {
- // Represented as string.
- //
- os << image_type << " " << mi.var << "value;"
- << "unsigned long " << mi.var << "size;"
- << "my_bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_set (member_info& mi)
- {
- // Represented as string.
- //
- os << image_type << " " << mi.var << "value;"
- << "unsigned long " << mi.var << "size;"
- << "my_bool " << mi.var << "null;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
<< endl;
}
private:
string image_type;
-
member_image_type member_image_type_;
};
entry<image_member> image_member_;
diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx
index 9390a99..f8510a2 100644
--- a/odb/relational/sqlite/source.cxx
+++ b/odb/relational/sqlite/source.cxx
@@ -18,49 +18,6 @@ namespace relational
{
namespace relational = relational::source;
- namespace
- {
- const char* integer_buffer_types[] =
- {
- "MYSQL_TYPE_TINY",
- "MYSQL_TYPE_SHORT",
- "MYSQL_TYPE_LONG", // *_bind_param() doesn't support INT24.
- "MYSQL_TYPE_LONG",
- "MYSQL_TYPE_LONGLONG"
- };
-
- const char* float_buffer_types[] =
- {
- "MYSQL_TYPE_FLOAT",
- "MYSQL_TYPE_DOUBLE"
- };
-
- const char* date_time_buffer_types[] =
- {
- "MYSQL_TYPE_DATE",
- "MYSQL_TYPE_TIME",
- "MYSQL_TYPE_DATETIME",
- "MYSQL_TYPE_TIMESTAMP",
- "MYSQL_TYPE_SHORT"
- };
-
- const char* char_bin_buffer_types[] =
- {
- "MYSQL_TYPE_STRING", // CHAR
- "MYSQL_TYPE_BLOB", // BINARY,
- "MYSQL_TYPE_STRING", // VARCHAR
- "MYSQL_TYPE_BLOB", // VARBINARY
- "MYSQL_TYPE_STRING", // TINYTEXT
- "MYSQL_TYPE_BLOB", // TINYBLOB
- "MYSQL_TYPE_STRING", // TEXT
- "MYSQL_TYPE_BLOB", // BLOB
- "MYSQL_TYPE_STRING", // MEDIUMTEXT
- "MYSQL_TYPE_BLOB", // MEDIUMBLOB
- "MYSQL_TYPE_STRING", // LONGTEXT
- "MYSQL_TYPE_BLOB" // LONGBLOB
- };
- }
-
//
// bind
//
@@ -126,116 +83,38 @@ namespace relational
virtual void
traverse_integer (member_info& mi)
{
- // While the is_unsigned should indicate whether the
- // buffer variable is unsigned, rather than whether the
- // database type is unsigned, in case of the image types,
- // this is the same.
- //
- os << b << ".buffer_type = " <<
- integer_buffer_types[mi.st->type - sql_type::TINYINT] << ";"
- << b << ".is_unsigned = " << (mi.st->unsign ? "1" : "0") << ";"
+ os << b << ".type = sqlite::binding::integer;"
<< b << ".buffer = &" << arg << "." << mi.var << "value;"
<< b << ".is_null = &" << arg << "." << mi.var << "null;";
}
virtual void
- traverse_float (member_info& mi)
+ traverse_real (member_info& mi)
{
- os << b << ".buffer_type = " <<
- float_buffer_types[mi.st->type - sql_type::FLOAT] << ";"
+ os << b << ".type = sqlite::binding::real;"
<< b << ".buffer = &" << arg << "." << mi.var << "value;"
<< b << ".is_null = &" << arg << "." << mi.var << "null;";
}
virtual void
- traverse_decimal (member_info& mi)
+ traverse_text (member_info& mi)
{
- os << b << ".buffer_type = MYSQL_TYPE_NEWDECIMAL;"
+ os << b << ".type = sqlite::binding::text;"
<< b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".buffer_length = static_cast<unsigned long> (" << endl
- << "" << arg << "." << mi.var << "value.capacity ());"
- << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ());"
<< b << ".is_null = &" << arg << "." << mi.var << "null;";
}
virtual void
- traverse_date_time (member_info& mi)
+ traverse_blob (member_info& mi)
{
- os << b << ".buffer_type = " <<
- date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
- << b << ".buffer = &" << arg << "." << mi.var << "value;";
-
- if (mi.st->type == sql_type::YEAR)
- os << b << ".is_unsigned = 0;";
-
- os << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_short_string (member_info& mi)
- {
- // MySQL documentation is quite confusing about the use of
- // buffer_length and length when it comes to input parameters.
- // Source code, however, tells us that it uses buffer_length
- // only if length is NULL.
- //
- os << b << ".buffer_type = " <<
- char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".buffer_length = static_cast<unsigned long> (" << endl
- << "" << arg << "." << mi.var << "value.capacity ());"
- << b << ".length = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_long_string (member_info& mi)
- {
- os << b << ".buffer_type = " <<
- char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ os << b << ".type = sqlite::binding::blob;"
<< b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".buffer_length = static_cast<unsigned long> (" << endl
- << "" << arg << "." << mi.var << "value.capacity ());"
- << b << ".length = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- // Treated as a BLOB.
- //
- os << b << ".buffer_type = MYSQL_TYPE_BLOB;"
- << b << ".buffer = " << arg << "." << mi.var << "value;"
- << b << ".buffer_length = static_cast<unsigned long> (" << endl
- << "sizeof (" << arg << "." << mi.var << "value));"
- << b << ".length = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_enum (member_info& mi)
- {
- // Represented as a string.
- //
- os << b << ".buffer_type = MYSQL_TYPE_STRING;"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".buffer_length = static_cast<unsigned long> (" << endl
- << "" << arg << "." << mi.var << "value.capacity ());"
- << b << ".length = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_set (member_info& mi)
- {
- // Represented as a string.
- //
- os << b << ".buffer_type = MYSQL_TYPE_STRING;"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".buffer_length = static_cast<unsigned long> (" << endl
- << "" << arg << "." << mi.var << "value.capacity ());"
- << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ());"
<< b << ".is_null = &" << arg << "." << mi.var << "null;";
}
@@ -298,82 +177,20 @@ namespace relational
virtual void
traverse_integer (member_info&)
{
- os << e << " = 0;"
+ os << e << " = false;"
<< endl;
}
virtual void
- traverse_float (member_info&)
+ traverse_real (member_info&)
{
- os << e << " = 0;"
+ os << e << " = false;"
<< endl;
}
virtual void
- traverse_decimal (member_info& mi)
+ traverse_string (member_info& mi)
{
- // @@ Optimization disabled.
- //
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_date_time (member_info&)
- {
- os << e << " = 0;"
- << endl;
- }
-
- virtual void
- traverse_short_string (member_info& mi)
- {
- // @@ Optimization disabled.
- //
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_long_string (member_info& mi)
- {
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_bit (member_info&)
- {
- os << e << " = 0;"
- << endl;
- }
-
- virtual void
- traverse_enum (member_info& mi)
- {
- // Represented as a string.
- //
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_set (member_info& mi)
- {
- // Represented as a string.
- //
os << "if (" << e << ")" << endl
<< "{"
<< "i." << mi.var << "value.capacity (i." << mi.var << "size);"
@@ -485,11 +302,10 @@ namespace relational
image_type = member_image_type_.image_type (mi.m);
db_type_id = member_database_type_id_.database_type_id (mi.m);
- os << "{"
- << "bool is_null;";
+ os << "{";
}
- traits = "mysql::value_traits<\n "
+ traits = "sqlite::value_traits<\n "
+ type + ",\n "
+ image_type + ",\n "
+ db_type_id + " >";
@@ -515,8 +331,7 @@ namespace relational
<< "throw null_pointer ();";
}
- os << "i." << mi.var << "null = is_null;"
- << "}";
+ os << "}";
}
}
@@ -534,113 +349,29 @@ namespace relational
traverse_integer (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
- }
-
- virtual void
- traverse_float (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
- }
-
- virtual void
- traverse_decimal (member_info& mi)
- {
- // @@ Optimization: can remove growth check if buffer is fixed.
- //
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
<< "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "size = static_cast<unsigned long> (size);"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ << "i." << mi.var << "null," << endl
+ << member << ");";
}
virtual void
- traverse_date_time (member_info& mi)
+ traverse_real (member_info& mi)
{
os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");";
- }
-
- virtual void
- traverse_short_string (member_info& mi)
- {
- // @@ Optimization: can remove growth check if buffer is fixed.
- //
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
<< "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "size = static_cast<unsigned long> (size);"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ << "i." << mi.var << "null," << endl
+ << member << ");";
}
virtual void
- traverse_long_string (member_info& mi)
+ traverse_string (member_info& mi)
{
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ os << "std::size_t cap (i." << mi.var << "value.capacity ());"
<< traits << "::set_image (" << endl
<< "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "size = static_cast<unsigned long> (size);"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- // Represented as a BLOB.
- //
- os << "std::size_t size (0);"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "sizeof (i." << mi.var << "value)," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "size = static_cast<unsigned long> (size);";
- }
-
- virtual void
- traverse_enum (member_info& mi)
- {
- // Represented as a string.
- //
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "size = static_cast<unsigned long> (size);"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
- }
-
- virtual void
- traverse_set (member_info& mi)
- {
- // Represented as a string.
- //
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null," << endl
<< member << ");"
- << "i." << mi.var << "size = static_cast<unsigned long> (size);"
<< "grew = grew || (cap != i." << mi.var << "value.capacity ());";
}
@@ -735,7 +466,7 @@ namespace relational
db_type_id = member_database_type_id_.database_type_id (mi.m);
}
- traits = "mysql::value_traits<\n "
+ traits = "sqlite::value_traits<\n "
+ type + ",\n "
+ image_type + ",\n "
+ db_type_id + " >";
@@ -788,93 +519,25 @@ namespace relational
traverse_integer (member_info& mi)
{
os << traits << "::set_value (" << endl
- << member << ", i." << mi.var << "value, " <<
- "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_float (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << ", i." << mi.var << "value, " <<
- "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_decimal (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_date_time (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << ", i." << mi.var << "value, " <<
- "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_short_string (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_long_string (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- // Represented as a BLOB.
- //
- os << traits << "::set_value (" << endl
<< member << "," << endl
<< "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
<< "i." << mi.var << "null);"
<< endl;
}
virtual void
- traverse_enum (member_info& mi)
+ traverse_real (member_info& mi)
{
- // Represented as a string.
- //
os << traits << "::set_value (" << endl
<< member << "," << endl
<< "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
<< "i." << mi.var << "null);"
<< endl;
}
virtual void
- traverse_set (member_info& mi)
+ traverse_string (member_info& mi)
{
- // Represented as a string.
- //
os << traits << "::set_value (" << endl
<< member << "," << endl
<< "i." << mi.var << "value," << endl
diff --git a/odb/sql-token.cxx b/odb/sql-token.cxx
new file mode 100644
index 0000000..fe6b836
--- /dev/null
+++ b/odb/sql-token.cxx
@@ -0,0 +1,44 @@
+// file : odb/sql-token.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <odb/sql-token.hxx>
+
+using namespace std;
+
+static char punctuation_literals[] = {';', ',', '(', ')', '='};
+
+ostream&
+operator<< (ostream& os, sql_token const& t)
+{
+ switch (t.type ())
+ {
+ case sql_token::t_eos:
+ {
+ os << "<end-of-stream>";
+ break;
+ }
+ case sql_token::t_identifier:
+ {
+ os << t.identifier ();
+ break;
+ }
+ case sql_token::t_punctuation:
+ {
+ os << punctuation_literals[t.punctuation ()];
+ break;
+ }
+ case sql_token::t_string_lit:
+ case sql_token::t_int_lit:
+ case sql_token::t_float_lit:
+ {
+ os << t.literal ();
+ break;
+ }
+ }
+
+ return os;
+}
diff --git a/odb/sql-token.hxx b/odb/sql-token.hxx
index fabcc99..4d9c7a1 100644
--- a/odb/sql-token.hxx
+++ b/odb/sql-token.hxx
@@ -7,6 +7,7 @@
#define ODB_SQL_TOKEN_HXX
#include <string>
+#include <iosfwd>
#include <cstddef> // std::size_t
class sql_token
@@ -36,6 +37,8 @@ public:
public:
enum punctuation_type
{
+ // Keep synched with punctuation_literals in source file.
+ //
p_semi,
p_comma,
p_lparen,
@@ -74,6 +77,9 @@ private:
std::string str_;
};
+std::ostream&
+operator<< (std::ostream&, sql_token const&);
+
#include <odb/sql-token.ixx>
#endif // ODB_SQL_TOKEN_HXX