Skip to content

Conversation

@gargsaumya
Copy link
Contributor

@gargsaumya gargsaumya commented Sep 9, 2025

Work Item / Issue Reference

AB#34936
AB#34937

GitHub Issue: #<ISSUE_NUMBER>


Summary

This pull request enhances support for SQL Server MONEY and SMALLMONEY types in the Python driver, ensuring correct handling of boundary values, NULLs, and round-trip conversions. It also improves the encoding and decoding of decimal values and adds comprehensive tests to validate the new behavior.

MONEY and SMALLMONEY Type Handling:

  • Updated _map_sql_type in cursor.py to detect decimal values within MONEY and SMALLMONEY ranges and bind them as strings, falling back to generic numeric binding otherwise.

Decimal Value Encoding/Decoding:

  • Improved encoding of decimal values in ddbc_bindings.cpp by writing them as little-endian byte arrays, ensuring compatibility with SQL_NUMERIC_STRUCT.
  • Enhanced decoding of SQL DECIMAL/NUMERIC types to Python decimal.Decimal, with robust NULL and error handling during result fetching.

Testing Enhancements:

  • Added comprehensive tests in test_004_cursor.py to verify MONEY and SMALLMONEY handling, including:
    • Insert/fetch of typical, boundary, and NULL values
    • Round-trip conversion checks
    • Boundary value assertions
    • Validation that out-of-range and invalid values raise errors

Copilot AI review requested due to automatic review settings September 9, 2025 09:56
@github-actions github-actions bot added the pr-size: medium Moderate update size label Sep 9, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for SQL Server MONEY and SMALLMONEY data types in the Python driver by implementing proper binding, encoding/decoding of decimal values, and comprehensive test coverage.

Key Changes:

  • Enhanced decimal value handling to detect and bind MONEY/SMALLMONEY ranges as strings with fallback to numeric binding
  • Improved decimal encoding in C++ bindings using little-endian byte arrays for SQL_NUMERIC_STRUCT compatibility
  • Added robust NULL handling and error management for SQL DECIMAL/NUMERIC type conversions

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
tests/test_004_cursor.py Comprehensive test suite for MONEY/SMALLMONEY handling including boundary values, NULL handling, roundtrip conversion, and error cases
mssql_python/pybind/ddbc_bindings.cpp Enhanced decimal encoding to little-endian byte arrays and improved NULL/error handling for SQL DECIMAL/NUMERIC types
mssql_python/cursor.py Added MONEY/SMALLMONEY range detection with string binding for values within range, fallback to numeric binding otherwise

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@sumitmsft sumitmsft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments

Copy link
Contributor

@sumitmsft sumitmsft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments

@github-actions github-actions bot added pr-size: medium Moderate update size and removed pr-size: medium Moderate update size labels Sep 11, 2025
@github-actions github-actions bot added pr-size: medium Moderate update size and removed pr-size: medium Moderate update size labels Sep 11, 2025
@gargsaumya
Copy link
Contributor Author

Left a few comments

Thanks for the feedback! I have addressed the comments and I wanted to provide context for why we currently bind MONEY and SMALLMONEY as SQL_VARCHAR/SQL_C_CHAR rather than SQL_NUMERIC.

  1. Precision/scale issues with SQL_NUMERIC_STRUCT:
    In practice, many ODBC drivers including SQL Server’s return unexpected results when fetching MONEY/SMALLMONEY types using SQL_NUMERIC_STRUCT. A common behavior is that the precision may be reported as the maximum allowed (e.g., 38), and scale may be reported as 0, which discards fractional parts entirely. This was exactly the issue I faced.
  2. Overflow risk:
    Our current NumericData struct uses a 64-bit integer to hold val × 10^scale. While this works for most MONEY/SMALLMONEY values, it’s extremely sensitive to boundary cases and cannot support arbitrary precision numerics safely.

To avoid all this, we have bound these types as strings (SQL_VARCHAR/SQL_C_CHAR). SQL Server handles the implicit conversion from string to MONEY/SMALLMONEY on insert, and we can fetch numeric values as strings and then parse them to Python Decimal without losing precision. This approach also avoids integer overflow issue.
While exploring solutions, I found that Pyodbc follows the same strategy, it reads and writes values as text strings rather than using SQL_NUMERIC_STRUCT. This is also discussed in [this StackOverflow thread], where contributors note that fetching numeric/decimal/money via SQL_C_NUMERIC can discard fractional digits - precisely the problem I encountered.

@gargsaumya gargsaumya merged commit 99d7bd0 into main Sep 15, 2025
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: medium Moderate update size

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants