r-dbi / odbc

Compare 76c7293 ... +4 ... 1e773f6

No flags found

Use flags to group coverage reports by test type, project and/or folders.
Then setup custom commit statuses and notifications for each flag.

e.g., #unittest #integration

#production #enterprise

#frontend #backend

Learn more about Codecov Flags here.

Showing 2 of 4 files from the diff.

@@ -161,6 +161,28 @@
Loading
161 161
162 162
  Rcpp::List result_to_dataframe(nanodbc::result& r, int n_max = -1);
163 163
164 +
  /// \brief Safely gets data from the given column of the current rowset.
165 +
  ///
166 +
  /// There is a bug/limitation in ODBC drivers for SQL Server (and
167 +
  /// possibly others) which causes SQLBindCol() to never write
168 +
  /// SQL_NOT_NULL to the length/indicator buffer unless you also bind the
169 +
  /// data column. Since, any column can be unbound (think, for example
170 +
  /// columns coming after LONG data in the case of Microsoft's OEM
171 +
  /// ODBC driver) we also have to check for nullity after an attempt to
172 +
  /// nanodbc::get - this is when the null indicator gets set for unbound
173 +
  /// columns.  In the case when the null fallback is the same type
174 +
  /// as the data we are attempting to retrieve (int/NA_INTEGER,
175 +
  /// int64/NA_INTEGER64, double/NA_REAL, logical/NA_INTEGER), we can
176 +
  /// use the safe_get template below.  With others, for example
177 +
  /// std::string / NA_STRING, where the fallback is a SEXP, this
178 +
  /// check-for-nullity-after-get is coded directly in the assign_
179 +
  /// function.
180 +
  /// \param column short int position.
181 +
  /// \param fallback typename T value to use as fallback in case of null
182 +
  /// \param value nanodbc::result
183 +
  template <typename T>
184 +
  T safe_get(short column, T fallback, nanodbc::result& value);
185 +
164 186
  void assign_integer(
165 187
      Rcpp::List& out, size_t row, short column, nanodbc::result& value);
166 188
  void assign_integer64(

@@ -31,7 +31,6 @@
Loading
31 31
      execute();
32 32
    }
33 33
  }
34 -
  unbind_if_needed();
35 34
}
36 35
std::shared_ptr<odbc_connection> odbc_result::connection() const {
37 36
  return std::shared_ptr<odbc_connection>(c_);
@@ -171,6 +170,7 @@
Loading
171 170
  if (num_columns_ == 0) {
172 171
    return Rcpp::DataFrame();
173 172
  }
173 +
  unbind_if_needed();
174 174
  try {
175 175
    return result_to_dataframe(*r_, n_max);
176 176
  } catch (...) {
@@ -766,19 +766,43 @@
Loading
766 766
  return out;
767 767
}
768 768
769 +
template <typename T>
770 +
T odbc_result::safe_get(short column, T fallback, nanodbc::result& value) {
771 +
  T res;
772 +
  res = value.get<T>(column, fallback);
773 +
  if (value.is_null(column)) {
774 +
    res = fallback;
775 +
  }
776 +
  return res;
777 +
}
778 +
769 779
void odbc_result::assign_integer(
770 780
    Rcpp::List& out, size_t row, short column, nanodbc::result& value) {
771 -
  INTEGER(out[column])[row] = value.get<int>(column, NA_INTEGER);
781 +
782 +
  int res = safe_get<int>(column, NA_INTEGER, value);
783 +
  INTEGER(out[column])[row] = res;
772 784
}
773 785
void odbc_result::assign_integer64(
774 786
    Rcpp::List& out, size_t row, short column, nanodbc::result& value) {
775 -
  INTEGER64(out[column])[row] = value.get<int64_t>(column, NA_INTEGER64);
787 +
788 +
  int64_t res = safe_get<int64_t>(column, NA_INTEGER64, value);
789 +
  INTEGER64(out[column])[row] = res;
776 790
}
777 791
void odbc_result::assign_double(
778 792
    Rcpp::List& out, size_t row, short column, nanodbc::result& value) {
779 -
  REAL(out[column])[row] = value.get<double>(column, NA_REAL);
793 +
794 +
  double res = safe_get<double>(column, NA_REAL, value);
795 +
  REAL(out[column])[row] = res;
796 +
}
797 +
798 +
void odbc_result::assign_logical(
799 +
    Rcpp::List& out, size_t row, short column, nanodbc::result& value) {
800 +
801 +
  int res = safe_get<int>(column, NA_LOGICAL, value);
802 +
  LOGICAL(out[column])[row] = res;
780 803
}
781 804
805 +
782 806
// Strings may be in the server's internal code page, so we need to re-encode
783 807
// in UTF-8 if necessary.
784 808
void odbc_result::assign_string(
@@ -788,16 +812,6 @@
Loading
788 812
  if (value.is_null(column)) {
789 813
    res = NA_STRING;
790 814
  } else {
791 -
    // There is a bug/limitation in ODBC drivers for SQL Server (and possibly
792 -
    // others)
793 -
    // which causes SQLBindCol() to never write SQL_NOT_NULL to the
794 -
    // length/indicator
795 -
    // buffer unless you also bind the data column. nanodbc's is_null() will
796 -
    // return
797 -
    // correct values for (n)varchar(max) columns when you ensure that
798 -
    // SQLGetData()
799 -
    // has been called for that column (i.e. after get() or get_ref() is
800 -
    // called).
801 815
    auto str = value.get<std::string>(column);
802 816
    if (value.is_null(column)) {
803 817
      res = NA_STRING;
@@ -821,12 +835,6 @@
Loading
821 835
  if (value.is_null(column)) {
822 836
    res = NA_STRING;
823 837
  } else {
824 -
    // There is a bug/limitation in ODBC drivers for SQL Server (and
825 -
    // possibly others) which causes SQLBindCol() to never write
826 -
    // SQL_NOT_NULL to the length/indicator buffer unless you also bind the
827 -
    // data column. nanodbc's is_null() will return correct values for
828 -
    // (n)varchar(max) columns when you ensure that SQLGetData() has been
829 -
    // called for that column (i.e. after get() or get_ref() is called).
830 838
    auto str = value.get<std::string>(column);
831 839
    if (value.is_null(column)) {
832 840
      res = NA_STRING;
@@ -845,7 +853,11 @@
Loading
845 853
    res = NA_REAL;
846 854
  } else {
847 855
    auto ts = value.get<nanodbc::timestamp>(column);
848 -
    res = as_double(ts);
856 +
    if (value.is_null(column)) {
857 +
      res = NA_REAL;
858 +
    } else {
859 +
      res = as_double(ts);
860 +
    }
849 861
  }
850 862
851 863
  REAL(out[column])[row] = res;
@@ -858,7 +870,11 @@
Loading
858 870
    res = NA_REAL;
859 871
  } else {
860 872
    auto ts = value.get<nanodbc::date>(column);
861 -
    res = as_double(ts);
873 +
    if (value.is_null(column)) {
874 +
      res = NA_REAL;
875 +
    } else {
876 +
      res = as_double(ts);
877 +
    }
862 878
  }
863 879
864 880
  REAL(out[column])[row] = res / seconds_in_day_;
@@ -871,17 +887,16 @@
Loading
871 887
    res = NA_REAL;
872 888
  } else {
873 889
    auto ts = value.get<nanodbc::time>(column);
874 -
    res = ts.hour * 3600 + ts.min * 60 + ts.sec;
890 +
    if (value.is_null(column)) {
891 +
      res = NA_REAL;
892 +
    } else {
893 +
      res = ts.hour * 3600 + ts.min * 60 + ts.sec;
894 +
    }
875 895
  }
876 896
877 897
  REAL(out[column])[row] = res;
878 898
}
879 899
880 -
void odbc_result::assign_logical(
881 -
    Rcpp::List& out, size_t row, short column, nanodbc::result& value) {
882 -
  LOGICAL(out[column])[row] = value.get<int>(column, NA_LOGICAL);
883 -
}
884 -
885 900
void odbc_result::assign_raw(
886 901
    Rcpp::List& out, size_t row, short column, nanodbc::result& value) {
887 902

Everything is accounted for!

No changes detected that need to be reviewed.
What changes does Codecov check for?
Lines, not adjusted in diff, that have changed coverage data.
Files that introduced coverage data that had none before.
Files that have missing coverage data that once were tracked.
Files Coverage
R 68.26%
src 0.12% 87.15%
Project Totals (16 files) 79.65%
Loading