@@ -935,79 +935,19 @@ odbc_internal::StatusRecord ConvertFromTimeDSValue(DSValue const& src_dsval,
935935 return status_record;
936936}
937937
938- odbc_internal::StatusRecord ConvertTimestampStringToChar (
939- const std::string& timestamp_src_str,
940- void * dest_buf,
941- SQLLEN buffer_length,
942- SQLLEN * res_len) {
943-
944- constexpr SQLLEN k_timestamp_src_len = 64 ; // adjust if needed
945- auto * dest = reinterpret_cast <char *>(dest_buf);
946- StatusRecord status_record;
947-
948- if (buffer_length > k_timestamp_src_len) {
949- if (res_len) *res_len = k_timestamp_src_len;
950- std::strncpy (dest, timestamp_src_str.c_str (), k_timestamp_src_len);
951- dest[k_timestamp_src_len] = ' \0 ' ;
952- } else if (20 <= buffer_length && buffer_length <= k_timestamp_src_len) {
953- if (res_len) *res_len = buffer_length;
954- std::strncpy (dest, timestamp_src_str.c_str (), buffer_length - 1 );
955- dest[buffer_length - 1 ] = ' \0 ' ;
956- status_record = StatusRecord{SQLStates::k_01004 (), " Data truncated" };
957- } else {
958- status_record =
959- StatusRecord{SQLStates::k_22003 (), " Buffer length is insufficient" };
960- }
961- return status_record;
962- }
963-
964- odbc_internal::StatusRecord ConvertTimestampStringToWChar (
965- const std::string& timestamp_src_str,
966- void * dest_buf,
967- SQLLEN buffer_length,
968- SQLLEN * res_len) {
969-
970- StatusRecord status_record;
971- auto wstr_or = Utf8ToUtf16 (timestamp_src_str);
972- if (!wstr_or) {
973- return StatusRecord{SQLStates::k_HY000 (), " DSValueToWchar Conversion Failed" };
974- }
975-
976- std::vector<SQLWCHAR > wstr_data (wstr_or->begin (), wstr_or->end ());
977- wstr_data.emplace_back (L' \0 ' );
978-
979- auto * dest = reinterpret_cast <SQLWCHAR *>(dest_buf);
980-
981- if (buffer_length > static_cast <SQLLEN >(wstr_or->size ())) {
982- if (res_len) *res_len = wstr_or->size () * sizeof (SQLWCHAR );
983- std::memcpy (dest, wstr_data.data (), wstr_or->size () * sizeof (SQLWCHAR ));
984- dest[wstr_or->size ()] = L' \0 ' ;
985- } else if (20 <= buffer_length && buffer_length <= static_cast <SQLLEN >(wstr_or->size ())) {
986- if (res_len) *res_len = buffer_length * sizeof (SQLWCHAR );
987- std::memcpy (dest, wstr_data.data (), buffer_length * sizeof (SQLWCHAR ));
988- dest[buffer_length - 1 ] = L' \0 ' ;
989- status_record = StatusRecord{SQLStates::k_01004 (), " Data truncated" };
990- } else {
991- status_record =
992- StatusRecord{SQLStates::k_22003 (), " Buffer length is insufficient" };
993- }
994-
995- return status_record;
996- }
997938odbc_internal::StatusRecord ConvertFromTimestampDSValue (
998939 DSValue const & src_dsval, DataBuffer& dest_data) {
940+ using odbc_internal::SQLStates;
941+ using odbc_internal::StatusRecord;
999942
1000- using odbc_internal::SQLStates;
1001- using odbc_internal::StatusRecord;
1002-
1003- std::string str_val;
1004- DSValueToString (src_dsval, str_val);
1005- std::string timestamp_src_str;
943+ std::string str_val;
944+ DSValueToString (src_dsval, str_val);
945+ std::string timestamp_src_str;
1006946
1007- SQLSMALLINT dest_type = dest_data.type ;
1008- SQLPOINTER dest_buf = dest_data.buf ;
1009- SQLLEN buffer_length = dest_data.buflen ;
1010- SQLLEN * res_len = dest_data.result_len ;
947+ SQLSMALLINT dest_type = dest_data.type ;
948+ SQLPOINTER dest_buf = dest_data.buf ;
949+ SQLLEN buffer_length = dest_data.buflen ;
950+ SQLLEN * res_len = dest_data.result_len ;
1011951
1012952 if (!dest_buf) {
1013953 return StatusRecord::Ok ();
@@ -1020,7 +960,64 @@ odbc_internal::StatusRecord ConvertFromTimestampDSValue(
1020960
1021961 StatusRecord status_record = StatusRecord::Ok ();
1022962
1023- switch (dest_type) {
963+ // --- Check for ISO string with 'T' and long fraction ---
964+ auto t_pos = str_val.find (' T' );
965+ if (t_pos != std::string::npos) {
966+ auto dot_pos = str_val.find (' .' , t_pos);
967+ bool long_fraction = false ;
968+
969+ if (dot_pos != std::string::npos) {
970+ std::size_t fraction_length = str_val.size () - dot_pos - 1 ;
971+ if (!str_val.empty () && str_val.back () == ' Z' ) --fraction_length;
972+ if (fraction_length > 9 ) long_fraction = true ;
973+ }
974+
975+ if (long_fraction) {
976+ // Trim 'Z' and replace 'T' with space
977+ if (!str_val.empty () && str_val.back () == ' Z' ) str_val.pop_back ();
978+ str_val[t_pos] = ' ' ;
979+ timestamp_src_str = str_val;
980+
981+ switch (dest_type) {
982+ case SQL_C_CHAR :
983+ return ConvertTimestampStringToChar (timestamp_src_str, dest_buf,
984+ buffer_length, res_len);
985+ case SQL_C_WCHAR :
986+ return ConvertTimestampStringToWChar (timestamp_src_str, dest_buf,
987+ buffer_length, res_len);
988+ default :
989+ LOG (ERROR ) << " ConvertFromTimestampDSValue:: Conversion unsupported "
990+ " for picosecond for C-type: "
991+ << dest_type;
992+ return StatusRecord{SQLStates::k_HY000 (),
993+ " Conversion unsupported for picosecond" };
994+ }
995+ }
996+ }
997+ // Conversion for unix epoch time
998+ bool looks_like_float_epoch = false ;
999+
1000+ try {
1001+ size_t idx = 0 ;
1002+ std::stod (str_val, &idx);
1003+ if (idx == str_val.length ()) {
1004+ looks_like_float_epoch = true ;
1005+ }
1006+ } catch (...) {
1007+ looks_like_float_epoch = false ;
1008+ }
1009+ SQL_TIMESTAMP_STRUCT timestamp_src_struct;
1010+ if (looks_like_float_epoch) {
1011+ timestamp_src_str = FloatTimestampToString (str_val);
1012+ timestamp_src_struct = ConvertStrToTimestampStruct (timestamp_src_str);
1013+ } else {
1014+ DSValueToTimestamp (src_dsval, timestamp_src_struct);
1015+ timestamp_src_str = FormatTimestampToString (timestamp_src_struct);
1016+ }
1017+ int k_timestamp_src_len = static_cast <int >(timestamp_src_str.length ());
1018+ constexpr int kTimestampBinaryLength = sizeof (SQL_TIMESTAMP_STRUCT );
1019+
1020+ switch (dest_type) {
10241021 case SQL_C_CHAR : {
10251022 auto * dest = reinterpret_cast <char *>(dest_buf);
10261023 if (buffer_length > k_timestamp_src_len) {
@@ -2298,4 +2295,4 @@ StatusRecord ConvertFromRangeDSValue(DSValue const& src_dsval,
22982295 }
22992296}
23002297
2301- } // namespace google::cloud::odbc_bq_driver_internal
2298+ } // namespace google::cloud::odbc_bq_driver_internal
0 commit comments