1#include "duckdb/parser/expression/cast_expression.hpp"
2#include "duckdb/parser/expression/constant_expression.hpp"
3#include "duckdb/parser/expression/function_expression.hpp"
4#include "duckdb/parser/transformer.hpp"
5#include "duckdb/common/operator/cast_operators.hpp"
6
7namespace duckdb {
8
9unique_ptr<ParsedExpression> Transformer::TransformInterval(duckdb_libpgquery::PGIntervalConstant &node) {
10 // handle post-fix notation of INTERVAL
11
12 // three scenarios
13 // interval (expr) year
14 // interval 'string' year
15 // interval int year
16 unique_ptr<ParsedExpression> expr;
17 switch (node.val_type) {
18 case duckdb_libpgquery::T_PGAExpr:
19 expr = TransformExpression(node: node.eval);
20 break;
21 case duckdb_libpgquery::T_PGString:
22 expr = make_uniq<ConstantExpression>(args: Value(node.sval));
23 break;
24 case duckdb_libpgquery::T_PGInteger:
25 expr = make_uniq<ConstantExpression>(args: Value(node.ival));
26 break;
27 default:
28 throw InternalException("Unsupported interval transformation");
29 }
30
31 if (!node.typmods) {
32 return make_uniq<CastExpression>(args: LogicalType::INTERVAL, args: std::move(expr));
33 }
34
35 int32_t mask = PGPointerCast<duckdb_libpgquery::PGAConst>(ptr: node.typmods->head->data.ptr_value)->val.val.ival;
36 // these seemingly random constants are from datetime.hpp
37 // they are copied here to avoid having to include this header
38 // the bitshift is from the function INTERVAL_MASK in the parser
39 constexpr int32_t MONTH_MASK = 1 << 1;
40 constexpr int32_t YEAR_MASK = 1 << 2;
41 constexpr int32_t DAY_MASK = 1 << 3;
42 constexpr int32_t HOUR_MASK = 1 << 10;
43 constexpr int32_t MINUTE_MASK = 1 << 11;
44 constexpr int32_t SECOND_MASK = 1 << 12;
45 constexpr int32_t MILLISECOND_MASK = 1 << 13;
46 constexpr int32_t MICROSECOND_MASK = 1 << 14;
47
48 // we need to check certain combinations
49 // because certain interval masks (e.g. INTERVAL '10' HOURS TO DAYS) set multiple bits
50 // for now we don't support all of the combined ones
51 // (we might add support if someone complains about it)
52
53 string fname;
54 LogicalType target_type;
55 if (mask & YEAR_MASK && mask & MONTH_MASK) {
56 // DAY TO HOUR
57 throw ParserException("YEAR TO MONTH is not supported");
58 } else if (mask & DAY_MASK && mask & HOUR_MASK) {
59 // DAY TO HOUR
60 throw ParserException("DAY TO HOUR is not supported");
61 } else if (mask & DAY_MASK && mask & MINUTE_MASK) {
62 // DAY TO MINUTE
63 throw ParserException("DAY TO MINUTE is not supported");
64 } else if (mask & DAY_MASK && mask & SECOND_MASK) {
65 // DAY TO SECOND
66 throw ParserException("DAY TO SECOND is not supported");
67 } else if (mask & HOUR_MASK && mask & MINUTE_MASK) {
68 // DAY TO SECOND
69 throw ParserException("HOUR TO MINUTE is not supported");
70 } else if (mask & HOUR_MASK && mask & SECOND_MASK) {
71 // DAY TO SECOND
72 throw ParserException("HOUR TO SECOND is not supported");
73 } else if (mask & MINUTE_MASK && mask & SECOND_MASK) {
74 // DAY TO SECOND
75 throw ParserException("MINUTE TO SECOND is not supported");
76 } else if (mask & YEAR_MASK) {
77 // YEAR
78 fname = "to_years";
79 target_type = LogicalType::INTEGER;
80 } else if (mask & MONTH_MASK) {
81 // MONTH
82 fname = "to_months";
83 target_type = LogicalType::INTEGER;
84 } else if (mask & DAY_MASK) {
85 // DAY
86 fname = "to_days";
87 target_type = LogicalType::INTEGER;
88 } else if (mask & HOUR_MASK) {
89 // HOUR
90 fname = "to_hours";
91 target_type = LogicalType::BIGINT;
92 } else if (mask & MINUTE_MASK) {
93 // MINUTE
94 fname = "to_minutes";
95 target_type = LogicalType::BIGINT;
96 } else if (mask & SECOND_MASK) {
97 // SECOND
98 fname = "to_seconds";
99 target_type = LogicalType::BIGINT;
100 } else if (mask & MILLISECOND_MASK) {
101 // MILLISECOND
102 fname = "to_milliseconds";
103 target_type = LogicalType::BIGINT;
104 } else if (mask & MICROSECOND_MASK) {
105 // SECOND
106 fname = "to_microseconds";
107 target_type = LogicalType::BIGINT;
108 } else {
109 throw InternalException("Unsupported interval post-fix");
110 }
111 // first push a cast to the target type
112 expr = make_uniq<CastExpression>(args&: target_type, args: std::move(expr));
113 // now push the operation
114 vector<unique_ptr<ParsedExpression>> children;
115 children.push_back(x: std::move(expr));
116 return make_uniq<FunctionExpression>(args&: fname, args: std::move(children));
117}
118
119} // namespace duckdb
120