Skip to content

Commit d3b914d

Browse files
committed
tests added
1 parent 1b32678 commit d3b914d

File tree

2 files changed

+50
-92
lines changed

2 files changed

+50
-92
lines changed

main.py

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,20 @@
22
from mssql_python import setup_logging
33
import os
44
import decimal
5-
# import pyodbc
65

76
setup_logging('stdout')
87

9-
# conn_str = os.getenv("DB_CONNECTION_STRING")
10-
conn_str = "DRIVER={ODBC Driver 18 for SQL Server};Server=Saumya;DATABASE=master;UID=sa;PWD=HappyPass1234;Trust_Connection=yes;TrustServerCertificate=yes;"
11-
8+
conn_str = os.getenv("DB_CONNECTION_STRING")
129
conn = connect(conn_str)
1310

14-
# # conn.autocommit = True
15-
16-
# cursor = conn.cursor()
17-
# cursor.execute("SELECT database_id, name from sys.databases;")
18-
# rows = cursor.fetchall()
19-
20-
# for row in rows:
21-
# print(f"Database ID: {row[0]}, Name: {row[1]}")
22-
23-
# cursor.close()
24-
# conn.close()
25-
26-
from datetime import datetime, timezone, timedelta
11+
# conn.autocommit = True
2712

28-
# Connect and get cursor
2913
cursor = conn.cursor()
14+
cursor.execute("SELECT database_id, name from sys.databases;")
15+
rows = cursor.fetchall()
3016

31-
# Create table (drop if exists)
32-
cursor.execute("""
33-
IF OBJECT_ID('dbo.test_datetimeoffset', 'U') IS NOT NULL
34-
DROP TABLE dbo.test_datetimeoffset;
35-
36-
CREATE TABLE dbo.test_datetimeoffset (
37-
id INT PRIMARY KEY,
38-
dt DATETIMEOFFSET
39-
)
40-
""")
41-
42-
# Insert a row
43-
dt_offset = datetime(2025, 9, 9, 15, 30, 45, 123456, tzinfo=timezone(timedelta(hours=5, minutes=30)))
44-
cursor.execute("INSERT INTO test_datetimeoffset (id, dt) VALUES (?, ?)", 1, dt_offset)
45-
conn.commit()
46-
print("Insertion done. Verify in SSMS.")
47-
48-
# --- Fetch the row ---
49-
cursor.execute("SELECT id, dt FROM dbo.test_datetimeoffset WHERE id = ?", 1)
50-
row = cursor.fetchone()
51-
52-
if row:
53-
fetched_id, fetched_dt = row
54-
print(f"Fetched ID: {fetched_id}")
55-
print(f"Fetched DATETIMEOFFSET: {fetched_dt} (tzinfo: {fetched_dt.tzinfo})")
17+
for row in rows:
18+
print(f"Database ID: {row[0]}, Name: {row[1]}")
5619

57-
# Optional check
58-
if fetched_dt == dt_offset:
59-
print("✅ Fetch successful: Datetime matches inserted value")
60-
else:
61-
print("⚠️ Fetch mismatch: Datetime does not match inserted value")
62-
else:
63-
print("No row fetched.")
20+
cursor.close()
21+
conn.close()

mssql_python/pybind/ddbc_bindings.cpp

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -525,51 +525,51 @@ SQLRETURN BindParameters(SQLHANDLE hStmt, const py::list& params,
525525
if (!py::isinstance(param, datetimeType)) {
526526
ThrowStdException(MakeParamMismatchErrorStr(paramInfo.paramCType, paramIndex));
527527
}
528-
if (paramInfo.paramSQLType == SQL_TIMESTAMP) {
529-
// Handle naive datetime
530-
SQL_TIMESTAMP_STRUCT* tsPtr = AllocateParamBuffer<SQL_TIMESTAMP_STRUCT>(paramBuffers);
531-
tsPtr->year = static_cast<SQLSMALLINT>(param.attr("year").cast<int>());
532-
tsPtr->month = static_cast<SQLUSMALLINT>(param.attr("month").cast<int>());
533-
tsPtr->day = static_cast<SQLUSMALLINT>(param.attr("day").cast<int>());
534-
tsPtr->hour = static_cast<SQLUSMALLINT>(param.attr("hour").cast<int>());
535-
tsPtr->minute = static_cast<SQLUSMALLINT>(param.attr("minute").cast<int>());
536-
tsPtr->second = static_cast<SQLUSMALLINT>(param.attr("second").cast<int>());
537-
tsPtr->fraction = static_cast<SQLUINTEGER>(param.attr("microsecond").cast<int>() * 1000);
538-
dataPtr = static_cast<void*>(tsPtr);
539-
}
540-
else if (paramInfo.paramSQLType == SQL_SS_TIMESTAMPOFFSET) {
541-
// Handle tz-aware datetime → SQL_DATETIMEOFFSET
542-
SQL_SS_TIMESTAMPOFFSET_STRUCT* dtoPtr = AllocateParamBuffer<SQL_SS_TIMESTAMPOFFSET_STRUCT>(paramBuffers);
543-
int year = param.attr("year").cast<int>();
544-
if (year < 1753 || year > 9999) {
545-
ThrowStdException("Date out of range for SQL Server (1753-9999) at paramIndex " + std::to_string(paramIndex));
546-
}
547-
dtoPtr->year = static_cast<SQLSMALLINT>(year);
548-
dtoPtr->month = static_cast<SQLUSMALLINT>(param.attr("month").cast<int>());
549-
dtoPtr->day = static_cast<SQLUSMALLINT>(param.attr("day").cast<int>());
550-
dtoPtr->hour = static_cast<SQLUSMALLINT>(param.attr("hour").cast<int>());
551-
dtoPtr->minute = static_cast<SQLUSMALLINT>(param.attr("minute").cast<int>());
552-
dtoPtr->second = static_cast<SQLUSMALLINT>(param.attr("second").cast<int>());
553-
dtoPtr->fraction = static_cast<SQLUINTEGER>(param.attr("microsecond").cast<int>() * 1000);
554-
555-
py::object tzinfo = param.attr("tzinfo");
556-
if (tzinfo.is_none()) {
557-
ThrowStdException("Datetime object must have tzinfo for DATETIMEOFFSET at paramIndex " + std::to_string(paramIndex));
558-
}
528+
// Checking if the object has a timezone
529+
py::object tzinfo = param.attr("tzinfo");
530+
if (tzinfo.is_none()) {
531+
ThrowStdException("Datetime object must have tzinfo for SQL_C_SS_TIMESTAMPOFFSET at paramIndex " + std::to_string(paramIndex));
532+
}
559533

560-
py::object utcoffset = tzinfo.attr("utcoffset")(param);
561-
if (utcoffset.is_none()) {
562-
ThrowStdException("utcoffset is None for DATETIMEOFFSET at paramIndex " + std::to_string(paramIndex));
563-
}
534+
DateTimeOffset* dtoPtr = AllocateParamBuffer<DateTimeOffset>(paramBuffers);
564535

565-
int total_seconds = static_cast<int>(utcoffset.attr("total_seconds")().cast<double>());
566-
dtoPtr->timezone_hour = static_cast<SQLSMALLINT>(total_seconds / 3600);
567-
dtoPtr->timezone_minute = static_cast<SQLSMALLINT>((abs(total_seconds) % 3600) / 60);
568-
dataPtr = static_cast<void*>(dtoPtr);
569-
}
570-
else {
571-
ThrowStdException("Unsupported SQL type for timestamp at paramIndex " + std::to_string(paramIndex));
536+
dtoPtr->year = static_cast<SQLSMALLINT>(param.attr("year").cast<int>());
537+
dtoPtr->month = static_cast<SQLUSMALLINT>(param.attr("month").cast<int>());
538+
dtoPtr->day = static_cast<SQLUSMALLINT>(param.attr("day").cast<int>());
539+
dtoPtr->hour = static_cast<SQLUSMALLINT>(param.attr("hour").cast<int>());
540+
dtoPtr->minute = static_cast<SQLUSMALLINT>(param.attr("minute").cast<int>());
541+
dtoPtr->second = static_cast<SQLUSMALLINT>(param.attr("second").cast<int>());
542+
dtoPtr->fraction = static_cast<SQLUINTEGER>(param.attr("microsecond").cast<int>() * 1000);
543+
544+
py::object utcoffset = tzinfo.attr("utcoffset")(param);
545+
int total_seconds = static_cast<int>(utcoffset.attr("total_seconds")().cast<double>());
546+
std::div_t div_result = std::div(total_seconds, 3600);
547+
dtoPtr->timezone_hour = static_cast<SQLSMALLINT>(div_result.quot);
548+
dtoPtr->timezone_minute = static_cast<SQLSMALLINT>(div(div_result.rem, 60).quot);
549+
550+
dataPtr = static_cast<void*>(dtoPtr);
551+
bufferLength = sizeof(DateTimeOffset);
552+
strLenOrIndPtr = AllocateParamBuffer<SQLLEN>(paramBuffers);
553+
*strLenOrIndPtr = bufferLength;
554+
break;
555+
}
556+
case SQL_C_TYPE_TIMESTAMP: {
557+
py::object datetimeType = py::module_::import("datetime").attr("datetime");
558+
if (!py::isinstance(param, datetimeType)) {
559+
ThrowStdException(MakeParamMismatchErrorStr(paramInfo.paramCType, paramIndex));
572560
}
561+
SQL_TIMESTAMP_STRUCT* sqlTimestampPtr =
562+
AllocateParamBuffer<SQL_TIMESTAMP_STRUCT>(paramBuffers);
563+
sqlTimestampPtr->year = static_cast<SQLSMALLINT>(param.attr("year").cast<int>());
564+
sqlTimestampPtr->month = static_cast<SQLUSMALLINT>(param.attr("month").cast<int>());
565+
sqlTimestampPtr->day = static_cast<SQLUSMALLINT>(param.attr("day").cast<int>());
566+
sqlTimestampPtr->hour = static_cast<SQLUSMALLINT>(param.attr("hour").cast<int>());
567+
sqlTimestampPtr->minute = static_cast<SQLUSMALLINT>(param.attr("minute").cast<int>());
568+
sqlTimestampPtr->second = static_cast<SQLUSMALLINT>(param.attr("second").cast<int>());
569+
// SQL server supports in ns, but python datetime supports in µs
570+
sqlTimestampPtr->fraction = static_cast<SQLUINTEGER>(
571+
param.attr("microsecond").cast<int>() * 1000); // Convert µs to ns
572+
dataPtr = static_cast<void*>(sqlTimestampPtr);
573573
break;
574574
}
575575
case SQL_C_NUMERIC: {

0 commit comments

Comments
 (0)