in language-extensions/python/src/PythonDataSet.cpp [1333:1407]
void PythonOutputDataSet::RetrieveDateTimeColumnFromDataFrame(
string columnName,
SQLULEN &columnSize,
SQLSMALLINT &decimalDigits,
SQLSMALLINT &nullable)
{
LOG("PythonOutputDataSet::RetrieveDateTimeColumnFromDataFrame");
DateTimeStruct *columnData = nullptr;
SQLINTEGER *strLenOrNullMap = nullptr;
if (m_rowsNumber > 0)
{
columnData = new DateTimeStruct[m_rowsNumber];
strLenOrNullMap = new SQLINTEGER[m_rowsNumber];
}
decimalDigits = 0;
if constexpr (is_same_v<DateTimeStruct, SQL_TIMESTAMP_STRUCT>)
{
// Max datetime2 precision is 7 but python only supports up to microseconds
// https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetime2-transact-sql
//
decimalDigits = 6;
}
nullable = SQL_NO_NULLS;
columnSize = sizeof(DateTimeStruct);
// Get the column as a list of Timestamp objects.
//
string script = "list(" + m_name + "['" + columnName + "'])";
bp::list column = bp::extract<bp::list>(bp::eval(script.c_str(), m_mainNamespace));
for (SQLULEN row = 0; row < m_rowsNumber; ++row)
{
bp::object pyObj = column[row];
// Make sure the iterator is not pointing at Python None, or else it will crash on extract.
// Also check the object type for NaT (Not a Time), a special timestamp type,
// because that should be NULL in SQL as well.
//
if (!pyObj.is_none() &&
strcmp(pyObj.ptr()->ob_type->tp_name, "NaTType") != 0)
{
SQL_TIMESTAMP_STRUCT timestamp = ExtractTimestampFromPyObject(pyObj.ptr());
if constexpr (is_same_v<DateTimeStruct, SQL_TIMESTAMP_STRUCT>)
{
columnData[row] = timestamp;
}
else
{
columnData[row] = { timestamp.year, timestamp.month, timestamp.day };
}
strLenOrNullMap[row] = sizeof(DateTimeStruct);
}
else
{
strLenOrNullMap[row] = SQL_NULL_DATA;
nullable = SQL_NULLABLE;
}
}
if (m_rowsNumber > 0)
{
m_data.push_back(static_cast<SQLPOINTER>(columnData));
}
else
{
m_data.push_back(nullptr);
}
m_columnNullMap.push_back(strLenOrNullMap);
}