Skip to content

Commit d56eb09

Browse files
authored
Merge pull request #1 from detule/fixup/date_time_with_int_storage
date/time: handle r types with integer storage
2 parents d0f7d9e + 64d042c commit d56eb09

File tree

4 files changed

+76
-44
lines changed

4 files changed

+76
-44
lines changed

src/odbc_result.cpp

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,18 @@ void odbc_result::bind_columns(
106106
case logical_t:
107107
bind_logical(obj, data, column, start, size, buffers);
108108
break;
109-
case date_t:
110-
bind_date(obj, data, column, start, size, buffers);
109+
case date_int_t:
110+
bind_date<T, int>(obj, data, column, start, size, buffers);
111+
case date_double_t:
112+
bind_date<T, double>(obj, data, column, start, size, buffers);
111113
break;
112-
case datetime_t:
113-
bind_datetime(obj, data, column, start, size, buffers);
114+
case datetime_int_t:
115+
Rcpp::Rcout << "bindings int datetime";
116+
bind_datetime<T, int>(obj, data, column, start, size, buffers);
117+
break;
118+
case datetime_double_t:
119+
Rcpp::Rcout << "bindings double datetime";
120+
bind_datetime<T, double>(obj, data, column, start, size, buffers);
114121
break;
115122
case double_t:
116123
bind_double(obj, data, column, start, size, buffers);
@@ -385,7 +392,7 @@ void odbc_result::bind_raw(
385392
column, buffers.raws_[column], reinterpret_cast<bool*>(buffers.nulls_[column].data()));
386393
}
387394

388-
template<typename T>
395+
template<typename T, typename SourceType>
389396
void odbc_result::bind_datetime(
390397
T& obj,
391398
Rcpp::List const& data,
@@ -395,7 +402,7 @@ void odbc_result::bind_datetime(
395402
param_data& buffers) {
396403

397404
buffers.nulls_[column] = std::vector<uint8_t>(size, false);
398-
auto d = REAL(data[column]);
405+
auto d = (SourceType*)DATAPTR(data[column]);
399406

400407
nanodbc::timestamp ts;
401408
short precision = 3;
@@ -426,7 +433,7 @@ void odbc_result::bind_datetime(
426433
reinterpret_cast<bool*>(buffers.nulls_[column].data()));
427434
}
428435

429-
template<typename T>
436+
template<typename T, typename SourceType>
430437
void odbc_result::bind_date(
431438
T& obj,
432439
Rcpp::List const& data,
@@ -436,11 +443,11 @@ void odbc_result::bind_date(
436443
param_data& buffers) {
437444

438445
buffers.nulls_[column] = std::vector<uint8_t>(size, false);
439-
auto d = REAL(data[column]);
446+
auto d = (SourceType*)DATAPTR(data[column]);
440447

441448
nanodbc::date dt;
442449
for (size_t i = 0; i < size; ++i) {
443-
auto value = d[start + i] * seconds_in_day_;
450+
auto value = (double)d[start + i] * seconds_in_day_;
444451
if (ISNA(value)) {
445452
buffers.nulls_[column][i] = true;
446453
} else {
@@ -635,8 +642,10 @@ Rcpp::List odbc_result::create_dataframe(
635642
out[j] = Rf_allocVector(INTSXP, n);
636643
break;
637644
case integer64_t:
638-
case date_t:
639-
case datetime_t:
645+
case date_int_t:
646+
case date_double_t:
647+
case datetime_int_t:
648+
case datetime_double_t:
640649
case odbc::time_t:
641650
case odbc::double_t:
642651
out[j] = Rf_allocVector(REALSXP, n);
@@ -681,10 +690,12 @@ void odbc_result::add_classes(
681690
case integer64_t:
682691
x.attr("class") = Rcpp::CharacterVector::create("integer64");
683692
break;
684-
case date_t:
693+
case date_int_t:
694+
case date_double_t:
685695
x.attr("class") = Rcpp::CharacterVector::create("Date");
686696
break;
687-
case datetime_t:
697+
case datetime_int_t:
698+
case datetime_double_t:
688699
x.attr("class") = Rcpp::CharacterVector::create("POSIXct", "POSIXt");
689700
x.attr("tzone") = Rcpp::CharacterVector::create(c_->timezone_out_str());
690701
break;
@@ -712,19 +723,29 @@ std::vector<r_type> odbc_result::column_types(Rcpp::List const& list) {
712723
types.push_back(dataframe_t);
713724
continue;
714725
}
715-
switch (TYPEOF(list[i])) {
726+
auto r_type = TYPEOF(list[i]);
727+
switch (r_type) {
716728
case LGLSXP:
717729
types.push_back(logical_t);
718730
break;
719-
case INTSXP:
720-
types.push_back(integer_t);
731+
case INTSXP: {
732+
Rcpp::RObject x = list[i];
733+
if (x.inherits("Date")) {
734+
types.push_back(date_int_t);
735+
} else if (x.inherits("POSIXct")) {
736+
Rcpp::Rcout << "Pushing datetime_int_t";
737+
types.push_back(datetime_int_t);
738+
} else {
739+
types.push_back(integer_t);
740+
}
721741
break;
742+
}
722743
case REALSXP: {
723744
Rcpp::RObject x = list[i];
724745
if (x.inherits("Date")) {
725-
types.push_back(date_t);
746+
types.push_back(date_double_t);
726747
} else if (x.inherits("POSIXct")) {
727-
types.push_back(datetime_t);
748+
types.push_back(datetime_double_t);
728749
} else if (x.inherits("difftime")) {
729750
types.push_back(odbc::time_t);
730751
} else {
@@ -797,7 +818,7 @@ std::vector<r_type> odbc_result::column_types(nanodbc::result const& r) {
797818
// Date
798819
case SQL_DATE:
799820
case SQL_TYPE_DATE:
800-
types.push_back(date_t);
821+
types.push_back(date_double_t);
801822
break;
802823
// Time
803824
case SQL_TIME:
@@ -808,7 +829,7 @@ std::vector<r_type> odbc_result::column_types(nanodbc::result const& r) {
808829
case SQL_TIMESTAMP:
809830
case SQL_TYPE_TIMESTAMP:
810831
case SQL_SS_TIMESTAMPOFFSET:
811-
types.push_back(datetime_t);
832+
types.push_back(datetime_double_t);
812833
break;
813834
case SQL_CHAR:
814835
case SQL_VARCHAR:
@@ -875,10 +896,12 @@ Rcpp::List odbc_result::result_to_dataframe(nanodbc::result& r, int n_max) {
875896
}
876897
for (size_t col = 0; col < types.size(); ++col) {
877898
switch (types[col]) {
878-
case date_t:
899+
case date_int_t:
900+
case date_double_t:
879901
assign_date(out, row, col, r);
880902
break;
881-
case datetime_t:
903+
case datetime_double_t:
904+
case datetime_int_t:
882905
assign_datetime(out, row, col, r);
883906
break;
884907
case odbc::double_t:
@@ -1099,24 +1122,6 @@ size_t odbc_result::get_parameter_rows(Rcpp::List const& x) {
10991122

11001123
# define R_ODBC_INSTANTIATE_BINDS(type) \
11011124
template void odbc_result::bind_columns(type& obj, r_type, \
1102-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1103-
template void odbc_result::bind_logical(type& obj, \
1104-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1105-
template void odbc_result::bind_integer(type& obj, \
1106-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1107-
template void odbc_result::bind_double(type& obj, \
1108-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1109-
template void odbc_result::bind_string(type& obj, \
1110-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1111-
template void odbc_result::bind_raw(type& obj, \
1112-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1113-
template void odbc_result::bind_datetime(type& obj, \
1114-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1115-
template void odbc_result::bind_date(type& obj, \
1116-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1117-
template void odbc_result::bind_time(type& obj, \
1118-
Rcpp::List const&, short, size_t, size_t, param_data&); \
1119-
template void odbc_result::bind_df(type& obj, \
11201125
Rcpp::List const&, short, size_t, size_t, param_data&);
11211126
R_ODBC_INSTANTIATE_BINDS(nanodbc::statement);
11221127
R_ODBC_INSTANTIATE_BINDS(nanodbc::table_valued_parameter);

src/odbc_result.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class odbc_result {
152152
size_t size,
153153
param_data& buffers);
154154

155-
template<typename T>
155+
template<typename T, typename SourceType>
156156
void bind_datetime(
157157
T& obj,
158158
Rcpp::List const& data,
@@ -161,7 +161,7 @@ class odbc_result {
161161
size_t size,
162162
param_data& buffers);
163163

164-
template<typename T>
164+
template<typename T, typename SourceType>
165165
void bind_date(
166166
T& obj,
167167
Rcpp::List const& data,

src/r_types.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ typedef enum {
99
integer_t,
1010
integer64_t,
1111
double_t,
12-
date_t,
13-
datetime_t,
12+
date_int_t,
13+
date_double_t,
14+
datetime_int_t,
15+
datetime_double_t,
1416
time_t,
1517
string_t,
1618
ustring_t,

tests/testthat/test-driver-sql-server.R

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,28 @@ test_that("Table-valued parameters", {
465465
params = list(df.param, 100, "Lorem ipsum dolor sit amet"))
466466
expect_identical(res, expected)
467467
})
468+
469+
# #952: Binding date types that may have INTSXP OR REALSXP storage
470+
test_that("Variable date type storage", {
471+
con <- test_con("SQLSERVER")
472+
tbl_name <- "datevariablestoragetest"
473+
# INTSXP storage
474+
data <- data.frame(
475+
"dates" = seq.Date(from = as.Date("2020-01-01"), to = as.Date("2020-01-02"), by = "1 day"),
476+
"dtms" = seq.POSIXt(from = as.POSIXct("2020-01-01", tz = "UTC"), to = as.POSIXct("2020-01-02", tz = "UTC"), length.out = 2L)
477+
)
478+
# REALSXP storage
479+
data_real <- data.frame(
480+
"dates" = c(as.Date("2020-01-01"), as.Date("2020-01-02")),
481+
"dtms" = c(as.POSIXct("2020-01-01", tz = "UTC"), as.POSIXct("2020-01-02", tz = "UTC"))
482+
)
483+
# 1. Writing both should succeed
484+
# 2. They should both come back with REALSXP storage
485+
local_table(con, tbl_name, data)
486+
res <- dbReadTable(con, tbl_name)
487+
expect_identical(data_real, res)
488+
489+
DBI::dbWriteTable(con, tbl_name, data_real, overwrite = TRUE)
490+
res <- dbReadTable(con, tbl_name)
491+
expect_identical(data_real, res)
492+
})

0 commit comments

Comments
 (0)