Skip to content

Commit 231334c

Browse files
committed
Add Date32 support and promote it as the primary date type
ClickHouse's Date type covers only 1970-01-01 to 2149-06-06. Date32 extends that range to 1900-01-01 – 2299-12-31, matching what ODBC clients typically expect. By routing all SQL_TYPE_DATE bindings through Date32, the driver avoids silent truncation/failures for out-of-range dates.
1 parent af719e0 commit 231334c

File tree

7 files changed

+41
-8
lines changed

7 files changed

+41
-8
lines changed

driver/escaping/escape_sequences.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ std::optional<std::string> processDate(const StringView seq, Lexer & lex) {
422422
if (data.isInvalid()) {
423423
return std::nullopt;
424424
} else {
425-
return std::string("toDate(") + data.literal.to_string() + ")";
425+
return std::string("toDate32(") + data.literal.to_string() + ")";
426426
}
427427
}
428428

driver/test/datetime_it.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ INSTANTIATE_TEST_SUITE_P(
155155
"toDate('2020-03-25')", SQL_TYPE_DATE,
156156
"2020-03-25", SQL_TIMESTAMP_STRUCT{2020, 3, 25, 0, 0, 0, 0}
157157
},
158+
DateTimeParams{"Date32", "ODBCDriver2", "UTC",
159+
"toDate32('2299-12-31')", SQL_TYPE_DATE,
160+
"2299-12-31", SQL_TIMESTAMP_STRUCT{2299, 12, 31, 0, 0, 0, 0}
161+
},
158162
DateTimeParams{"DateTime", "ODBCDriver2", "UTC",
159163
"toDateTime('2020-03-25 12:11:22')", SQL_TYPE_TIMESTAMP,
160164
"2020-03-25 12:11:22", SQL_TIMESTAMP_STRUCT{2020, 3, 25, 12, 11, 22, 0}
@@ -210,6 +214,31 @@ INSTANTIATE_TEST_SUITE_P(
210214
}
211215
);
212216

217+
TEST_F(DateTime, DateAndDate32Compatibility)
218+
{
219+
auto query = fromUTF8<PTChar>(R"""(
220+
SELECT date FROM (
221+
SELECT CAST('2006-01-02' AS Date) AS date
222+
)
223+
WHERE date = ?
224+
)""");
225+
STMT_OK(SQLPrepare(hstmt, ptcharCast(query.data()), SQL_NTS));
226+
SQL_DATE_STRUCT expect = {
227+
.year = 2006,
228+
.month = 1,
229+
.day = 2,
230+
};
231+
SQLLEN indicator = sizeof(expect);
232+
STMT_OK(SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 0, 0, &expect, 0, &indicator));
233+
STMT_OK(SQLExecute(hstmt));
234+
STMT_OK(SQLFetch(hstmt));
235+
236+
SQL_DATE_STRUCT date = {0};
237+
indicator = 0;
238+
STMT_OK(SQLGetData(hstmt, 1, SQL_C_TYPE_DATE, &date, sizeof(date), &indicator));
239+
EXPECT_EQ(date, expect);
240+
}
241+
213242
TEST_F(DateTime, TimeAsTimestamp)
214243
{
215244
auto query = fromUTF8<PTChar>("SELECT CAST('15:04:05' AS Time) AS col SETTINGS enable_time_time64_type = 1");

driver/test/escape_sequences_ut.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ TEST(EscapeSequencesCase, ParameterizedFunctionCalls) {
287287
TEST(EscapeSequencesCase, DateTime) {
288288
ASSERT_EQ(replaceEscapeSequences("SELECT {t '15:04:05'}"), "SELECT cast('15:04:05' as Time)");
289289

290-
ASSERT_EQ(replaceEscapeSequences("SELECT {d '2017-01-01'}"), "SELECT toDate('2017-01-01')");
290+
ASSERT_EQ(replaceEscapeSequences("SELECT {d '2017-01-01'}"), "SELECT toDate32('2017-01-01')");
291291

292292
ASSERT_EQ(replaceEscapeSequences("SELECT {ts '2017-01-01 10:01:01'}"), "SELECT toDateTime64('2017-01-01 10:01:01', 9)");
293293
ASSERT_EQ(replaceEscapeSequences("SELECT {ts '2017-01-01 10:01:01.555'}"), "SELECT toDateTime64('2017-01-01 10:01:01.555', 9)");

driver/test/statement_parameter_binding_ut.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ TEST_F(StatementBindingTest, FromCCharToOtherTypes) {
9292
"select "
9393
"{odbc_positional_1:Int32}, "
9494
"{odbc_positional_2:Decimal(10, 2)}, "
95-
"{odbc_positional_3:Date}, "
95+
"{odbc_positional_3:Date32}, "
9696
"{odbc_positional_4:LowCardinality(String)}, "
9797
"{odbc_positional_5:DateTime64(9)}, "
9898
"{odbc_positional_6:UUID}");
@@ -184,7 +184,7 @@ TEST_F(StatementBindingTest, FromCDateTime) {
184184
auto [query, params] = execute();
185185
ASSERT_EQ(query,
186186
"select "
187-
"{odbc_positional_1:Nullable(Date)}, "
187+
"{odbc_positional_1:Nullable(Date32)}, "
188188
"{odbc_positional_2:LowCardinality(String)}, "
189189
"{odbc_positional_3:Nullable(DateTime64(9))}");
190190
ASSERT_EQ(params.size(), 3);

driver/test/type_info_it.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ TEST_F(TypeInfoTest, ClickhouseToSQLTypeMapping)
5555
{"String", "0", SQL_VARCHAR},
5656
{"FixedString(1)", "'0'", SQL_VARCHAR},
5757
{"Date", "0", SQL_TYPE_DATE},
58-
{"Date32", "0", SQL_VARCHAR},
58+
{"Date32", "0", SQL_TYPE_DATE},
5959
{"DateTime", "0", SQL_TYPE_TIMESTAMP},
6060
{"DateTime64", "0", SQL_TYPE_TIMESTAMP},
6161
{"UUID", "'00000000-0000-0000-0000-000000000000'", SQL_GUID},
@@ -237,7 +237,7 @@ TEST_F(TypeInfoTest, SQLGetTypeInfoResultSet)
237237
{{"String", SQL_WVARCHAR }, {max_size, "'", "'", na, na, na, na, SQL_VARCHAR, na, na, }},
238238
{{"FixedString",SQL_VARCHAR }, {max_size, "'", "'", length, na, na, na, SQL_VARCHAR, na, na, }},
239239
{{"Time", SQL_TYPE_TIME }, {8, na, na, na, na, na, na, SQL_DATE, 2, na, }},
240-
{{"Date", SQL_TYPE_DATE }, {10, na, na, na, na, na, na, SQL_DATE, 1, na, }},
240+
{{"Date32", SQL_TYPE_DATE }, {10, na, na, na, na, na, na, SQL_DATE, 1, na, }},
241241
{{"DateTime64", SQL_TYPE_TIMESTAMP}, {29, na, na, scale, na, 0, 9, SQL_DATE, 3, na, }},
242242
{{"DateTime", SQL_TYPE_TIMESTAMP}, {19, na, na, na, na, na, na, SQL_DATE, 3, na, }},
243243
{{"UUID", SQL_GUID }, {35, na, na, na, na, na, na, SQL_GUID, na, na, }},
@@ -396,6 +396,7 @@ TEST_F(TypeInfoTest, AllTypesColumns)
396396
{"FixedString(42)",{SQL_VARCHAR, "FixedString", 42, 0, na, na, false, SQL_VARCHAR, na, msz }},
397397
{"Time", {SQL_TYPE_TIME, "Time", 8, 0, na, na, false, SQL_DATE, 2, 6 }},
398398
{"Date", {SQL_TYPE_DATE, "Date", 10, 0, na, na, false, SQL_DATE, 1, 6 }},
399+
{"Date32", {SQL_TYPE_DATE, "Date32", 10, 0, na, na, false, SQL_DATE, 1, 6 }},
399400
{"DateTime", {SQL_TYPE_TIMESTAMP,"DateTime", 19, 0, 0, na, false, SQL_DATE, 3, 16 }},
400401
{"DateTime64", {SQL_TYPE_TIMESTAMP,"DateTime64", 29, 0, 3, na, false, SQL_DATE, 3, 16 }},
401402
{"DateTime64(9)", {SQL_TYPE_TIMESTAMP,"DateTime64", 29, 0, 9, na, false, SQL_DATE, 3, 16 }},

driver/utils/type_info.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ std::string convertCTypeToDataSourceType(const BoundTypeInfo & type_info) {
405405

406406
case SQL_C_DATE:
407407
case SQL_C_TYPE_DATE:
408-
type_name = set_nullability("Date");
408+
type_name = set_nullability("Date32");
409409
break;
410410

411411
case SQL_C_TIME:
@@ -519,7 +519,7 @@ std::string convertSQLTypeToDataSourceType(const BoundTypeInfo & type_info) {
519519
break;
520520

521521
case SQL_TYPE_DATE:
522-
type_name = set_nullability("Date");
522+
type_name = set_nullability("Date32");
523523
break;
524524

525525
case SQL_TYPE_TIME:

driver/utils/type_info.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum class DataSourceTypeId {
3434
String,
3535
FixedString,
3636
Date,
37+
Date32,
3738
Time,
3839
DateTime64,
3940
DateTime,
@@ -189,6 +190,8 @@ class TypeInfoCatalog
189190
{.type_id=FixedString, .type_name="FixedString", .data_type=SQL_VARCHAR, .column_size=string_max_size,
190191
.literal_wrapper="'", .create_params="length", .octet_length=string_max_size},
191192
{.type_id=Date, .type_name="Date", .data_type=SQL_TYPE_DATE, .column_size=10,
193+
.sql_data_type=SQL_DATE, .sql_datetime_sub=SQL_CODE_DATE, .octet_length=6, .hidden=true},
194+
{.type_id=Date32, .type_name="Date32", .data_type=SQL_TYPE_DATE, .column_size=10,
192195
.sql_data_type=SQL_DATE, .sql_datetime_sub=SQL_CODE_DATE, .octet_length=6},
193196
{.type_id=Time, .type_name="Time", .data_type=SQL_TYPE_TIME, .column_size=8,
194197
.sql_data_type=SQL_DATE, .sql_datetime_sub=SQL_CODE_TIME, .octet_length=6},

0 commit comments

Comments
 (0)