From 70492a2ffe88a8ba8393a895eee7716778bec84a Mon Sep 17 00:00:00 2001 From: Constantin Michael Date: Mon, 7 Nov 2011 15:15:27 +0200 Subject: Add support for Oracle INTERVAL temporal types --- odb/relational/oracle/context.cxx | 249 ++++++++++++++++++++++++-------------- 1 file changed, 156 insertions(+), 93 deletions(-) (limited to 'odb/relational/oracle/context.cxx') diff --git a/odb/relational/oracle/context.cxx b/odb/relational/oracle/context.cxx index 3fe3e6d..37ada79 100644 --- a/odb/relational/oracle/context.cxx +++ b/odb/relational/oracle/context.cxx @@ -191,26 +191,27 @@ namespace relational // NATIONAL CHAR VARYING (NVARCHAR2) // NCHAR VARYING (NVARCHAR2) // DOUBLE PRECISION (FLOAT(126)) + // INTERVAL YEAR TO MONTH + // INTERVAL DAY TO SECOND // enum state { - parse_name, + parse_identifier, parse_range, parse_done }; - state s (parse_name); + state s (parse_identifier); string prefix; + sql_token t (l.next ()); - for (sql_token t (l.next ()); - s != parse_done && t.type () != sql_token::t_eos; - t = l.next ()) + while (t.type () != sql_token::t_eos) { sql_token::token_type tt (t.type ()); switch (s) { - case parse_name: + case parse_identifier: { if (tt == sql_token::t_identifier) { @@ -219,15 +220,32 @@ namespace relational // // Numeric types. // - if (id == "NUMBER" || id == "DEC" || id == "DECIMAL") + if ((id == "NUMBER" || id == "DEC" || id == "DECIMAL") && + prefix.empty ()) { // DEC and DECIMAL are equivalent to NUMBER. // r.type = sql_type::NUMBER; + s = parse_range; } - else if (id == "INT" || - id == "INTEGER" || - id == "SMALLINT") + else if ((id == "DEC" || id == "DECIMAL" || id == "NUMERIC" || + id == "INT" || id == "INTEGER" || id == "SMALLINT") + && prefix.empty ()) + { + // DEC, DECIMAL, and NUMERIC are equivalent. They are + // equivalent to NUMBER in all ways except that they may not + // represent a floating point number. The scale defaults to + // zero. + // + r.type = sql_type::NUMBER; + r.range = true; + r.range = 38; + + s = parse_range; + } + else if ((id == "INT" || + id == "INTEGER" || + id == "SMALLINT") && prefix.empty ()) { // INT, INTEGER, and SMALLINT map to NUMBER(38). They may not // have range or scale explicitly specified. @@ -235,82 +253,114 @@ namespace relational r.type = sql_type::NUMBER; r.range = true; r.range_value = 38; + + s = parse_done; } // // Floating point types // - else if (id == "FLOAT") + else if (id == "FLOAT" && prefix.empty ()) { r.type = sql_type::FLOAT; r.range = true; r.range_value = 126; + + s = parse_range; } - else if (id == "DOUBLE") + else if (id == "DOUBLE" && prefix.empty ()) { - prefix += prefix.empty () ? "" : " "; - prefix += id; - - continue; + prefix = id; } else if (id == "PRECISION" && prefix == "DOUBLE") { r.type = sql_type::FLOAT; r.range = true; r.range_value = 126; + + s = parse_done; } - else if (id == "REAL") + else if (id == "REAL" && prefix.empty ()) { r.type = sql_type::FLOAT; r.range = true; r.range_value = 63; + + s = parse_done; } - else if (id == "BINARY_FLOAT") + else if (id == "BINARY_FLOAT" && prefix.empty ()) { r.type = sql_type::BINARY_FLOAT; + s = parse_done; } - else if (id == "BINARY_DOUBLE") + else if (id == "BINARY_DOUBLE" && prefix.empty ()) { r.type = sql_type::BINARY_DOUBLE; + s = parse_done; } // // Date-time types. // - else if (id == "DATE") + else if (id == "DATE" && prefix.empty ()) { r.type = sql_type::DATE; + s = parse_done; } - else if (id == "TIMESTAMP") + else if (id == "TIMESTAMP" && prefix.empty ()) { - prefix += prefix.empty () ? "" : " "; + prefix = id; + } + else if (id == "INTERVAL" && prefix.empty ()) + { + prefix = id; + } + else if (id == "YEAR" && prefix == "INTERVAL") + { + prefix += " "; prefix += id; - - continue; + s = parse_range; + } + else if (id == "DAY" && prefix == "INTERVAL") + { + prefix += " "; + prefix += id; + s = parse_range; + } + else if (id == "TO" && + (prefix == "INTERVAL YEAR" || + prefix == "INTERVAL DAY")) + { + prefix += " "; + prefix += id; + } + else if (id == "MONTH" && prefix == "INTERVAL YEAR TO") + { + r.type = sql_type::INTERVAL_YM; + s = parse_range; + } + else if (id == "SECOND" && prefix == "INTERVAL DAY TO") + { + r.type = sql_type::INTERVAL_DS; + s = parse_range; } // - // Timestamp with time zone. + // Timestamp with time zone (not supported). // else if (id == "WITH" && prefix == "TIMESTAMP") { - prefix += prefix.empty () ? "" : " "; + prefix += " "; prefix += id; - - continue; } else if (id == "TIME" && (prefix == "TIMESTAMP WITH" || prefix == "TIMESTAMP WITH LOCAL")) { - prefix += prefix.empty () ? "" : " "; + prefix += " "; prefix += id; - - continue; } else if (id == "LOCAL" && prefix == "TIMESTAMP WITH") { - prefix += prefix.empty () ? "" : " "; + prefix += " "; prefix += id; - - continue; } else if (id == "ZONE" && (prefix == "TIMESTAMP WITH LOCAL TIME" || @@ -327,22 +377,16 @@ namespace relational { prefix += prefix.empty () ? "" : " "; prefix += id; - - continue; } else if (id == "CHARACTER") { prefix += prefix.empty () ? "" : " "; prefix += id; - - continue; } else if (id == "NCHAR") { prefix += prefix.empty () ? "" : " "; prefix += id; - - continue; } else if (id == "VARCHAR" || id == "VARCHAR2") { @@ -350,10 +394,12 @@ namespace relational // However, this may change in future versions. // r.type = sql_type::VARCHAR2; + s = parse_range; } else if (id == "NVARCHAR2") { r.type = sql_type::NVARCHAR2; + s = parse_range; } else if (id == "VARYING") { @@ -365,32 +411,35 @@ namespace relational prefix == "NATIONAL CHAR" || prefix == "NATIONAL CHARACTER") r.type = sql_type::NVARCHAR2; + + s = parse_range; } - else if (id == "NATIONAL") + else if (id == "NATIONAL" && prefix.empty ()) { - prefix += prefix.empty () ? "" : " "; - prefix += id; - - continue; + prefix = id; } - else if (id == "RAW") + else if (id == "RAW" && prefix.empty ()) { r.type = sql_type::RAW; + s = parse_range; } // // LOB types. // - else if (id == "BLOB") + else if (id == "BLOB" && prefix.empty ()) { r.type = sql_type::BLOB; + s = parse_done; } - else if (id == "CLOB") + else if (id == "CLOB" && prefix.empty ()) { r.type = sql_type::CLOB; + s = parse_done; } - else if (id == "NCLOB") + else if (id == "NCLOB" && prefix.empty ()) { r.type = sql_type::NCLOB; + s = parse_done; } // // LONG types. @@ -402,61 +451,59 @@ namespace relational } else { - if (tt == sql_token::t_identifier) - { - throw invalid_sql_type ( - "unknown Oracle type '" + t.identifier () + "'"); - } - else - throw invalid_sql_type ("expected Oracle type name"); + throw invalid_sql_type ( + "unknown Oracle type '" + t.identifier () + "'"); } - } - // Some prefixes can also be type names if not followed - // by the actual type name. - // - if (r.type != sql_type::invalid) t = l.next (); - else if (!prefix.empty ()) + continue; + } + else { - if (prefix == "CHAR" || prefix == "CHARACTER") - { - r.type = sql_type::CHAR; - r.range = true; - r.range_value = 1; - r.byte_semantics = true; - } - else if (prefix == "NCHAR" || - prefix == "NATIONAL CHAR" || - prefix == "NATIONAL CHARACTER") - { - r.type = sql_type::NCHAR; - r.range = true; - r.range_value = 1; - r.byte_semantics = false; - } - else if (prefix == "TIMESTAMP") + // Some prefixes can also be type names if not followed + // by the actual type name. + // + if (!prefix.empty ()) { - r.type = sql_type::TIMESTAMP; - r.range = true; - r.range_value = 6; + if (prefix == "CHAR" || prefix == "CHARACTER") + { + r.type = sql_type::CHAR; + r.range = true; + r.range_value = 1; + r.byte_semantics = true; + } + else if (prefix == "NCHAR" || + prefix == "NATIONAL CHAR" || + prefix == "NATIONAL CHARACTER") + { + r.type = sql_type::NCHAR; + r.range = true; + r.range_value = 1; + r.byte_semantics = false; + } + else if (prefix == "TIMESTAMP") + { + r.type = sql_type::TIMESTAMP; + r.range = true; + r.range_value = 6; + } } if (r.type == sql_type::invalid) { - if (tt == sql_token::t_identifier) - { - throw invalid_sql_type ( - "unknown Oracle type '" + t.identifier () + "'"); - } - else - throw invalid_sql_type ("expected Oracle type name"); + throw invalid_sql_type ( + "unknown Oracle type '" + t.identifier () + "'"); } + + // All of the possible types handled in this block can take an + // optional range specifier. Set the state and fall through to + // the parse_range handler. + // + s = parse_range; } // Fall through. // - s = parse_range; } case parse_range: { @@ -493,6 +540,15 @@ namespace relational // if (t.punctuation () == sql_token::p_comma) { + // If we are parsing the precision of a TIMESTAMP or INTERVAL + // type, there should be no scale present. + // + if (r.type == sql_type::TIMESTAMP || + string (prefix, 0, 8) == "INTERVAL") + throw invalid_sql_type ( + "invalid precision value '" + t.literal () + "' in " + "Oracle type declaration"); + t = l.next (); if (t.type () != sql_token::t_int_lit) @@ -528,6 +584,8 @@ namespace relational "invalid keyword '" + t.literal () + "' in Oracle " "type declaration"); } + + t = l.next (); } if (t.punctuation () != sql_token::p_rparen) @@ -535,14 +593,19 @@ namespace relational throw invalid_sql_type ( "expected ')' in Oracle type declaration"); } + else + t = l.next (); } - s = parse_done; - break; + s = parse_identifier; + continue; } case parse_done: { - assert (false); + throw invalid_sql_type ( + "invalid keyword '" + t.literal () + "' in Oracle " + "type declaration"); + break; } } -- cgit v1.1