Skip to content

Conversation

@bewithgaurav
Copy link
Collaborator

Work Item / Issue Reference

AB#41884

GitHub Issue: #<ISSUE_NUMBER>


Summary

This pull request refactors the _bulkcopy method in mssql_python/cursor.py to improve its interface and documentation. Instead of accepting arbitrary keyword arguments, the method now explicitly declares all supported bulk copy options as named parameters with type hints and default values.

@github-actions github-actions bot added the pr-size: small Minimal code update label Jan 30, 2026
…IDE discoverability

- All options now explicit in function signature
- Pass params directly to pycore (no kwargs dict conversion)
- Matches mssql-tds explicit params API

Based on community feedback from discussion #414
@bewithgaurav bewithgaurav force-pushed the bewithgaurav/fix-bulkcopy-kwargs branch from c90f0f1 to fa2eab4 Compare January 30, 2026 12:39
@bewithgaurav bewithgaurav marked this pull request as ready for review January 30, 2026 12:40
Copilot AI review requested due to automatic review settings January 30, 2026 12:40
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

Refactors Cursor._bulkcopy to replace **kwargs bulk-copy options with an explicit, type-hinted parameter list and updated docstring.

Changes:

  • Replaced **kwargs in _bulkcopy with explicit parameters (e.g., batch_size, timeout, column_mappings, and bulk-copy flags).
  • Expanded _bulkcopy docstring to document supported bulk copy options.
  • Updated the pycore_cursor.bulkcopy(...) invocation to pass the new explicit parameters.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


column_mappings: Maps source data columns to target table column names.
Each tuple is (source, target_column_name) where:
- source: Column name (str) or 0-based index (int) in the source data
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The doc/type for column_mappings now allows a string “source column name”, but the data contract above still says each row is a tuple/list (which has no column names). Please clarify the supported row types and mapping behavior, or restrict column_mappings back to index-based mapping to avoid misleading API/docs.

Suggested change
- source: Column name (str) or 0-based index (int) in the source data
- source: 0-based index (int) in the source data

Copilot uses AI. Check for mistakes.
Comment on lines +2622 to +2633
result = pycore_cursor.bulkcopy(
table_name,
iter(data),
batch_size=batch_size,
timeout=timeout,
column_mappings=column_mappings,
keep_identity=keep_identity,
check_constraints=check_constraints,
table_lock=table_lock,
keep_nulls=keep_nulls,
fire_triggers=fire_triggers,
use_internal_transaction=use_internal_transaction,
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The refactor changes behavior by always passing parameters like keep_identity, check_constraints, etc. to pycore_cursor.bulkcopy, even when they are None. Previously, omitted options would not be forwarded at all (via **kwargs), allowing the underlying library defaults to apply. To preserve semantics (and avoid potential TypeError if the core API expects bool), build a kwargs/options dict and only include values that are not None (or change defaults to explicit booleans).

Suggested change
result = pycore_cursor.bulkcopy(
table_name,
iter(data),
batch_size=batch_size,
timeout=timeout,
column_mappings=column_mappings,
keep_identity=keep_identity,
check_constraints=check_constraints,
table_lock=table_lock,
keep_nulls=keep_nulls,
fire_triggers=fire_triggers,
use_internal_transaction=use_internal_transaction,
bulkcopy_kwargs = {}
if batch_size is not None:
bulkcopy_kwargs["batch_size"] = batch_size
if timeout is not None:
bulkcopy_kwargs["timeout"] = timeout
if column_mappings is not None:
bulkcopy_kwargs["column_mappings"] = column_mappings
if keep_identity is not None:
bulkcopy_kwargs["keep_identity"] = keep_identity
if check_constraints is not None:
bulkcopy_kwargs["check_constraints"] = check_constraints
if table_lock is not None:
bulkcopy_kwargs["table_lock"] = table_lock
if keep_nulls is not None:
bulkcopy_kwargs["keep_nulls"] = keep_nulls
if fire_triggers is not None:
bulkcopy_kwargs["fire_triggers"] = fire_triggers
if use_internal_transaction is not None:
bulkcopy_kwargs["use_internal_transaction"] = use_internal_transaction
result = pycore_cursor.bulkcopy(
table_name,
iter(data),
**bulkcopy_kwargs,

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we're sending appropriate params, resolving this

self,
table_name: str,
data: Iterable[Union[Tuple, List]],
batch_size: Optional[int] = None,
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

batch_size is typed as Optional[int], but the validation accepts floats (isinstance(batch_size, (int, float))) while the error message says “positive integer”. Please align the type hint, validation, and message (either require an int, or explicitly support non-integer values and document why).

Suggested change
batch_size: Optional[int] = None,
batch_size: Optional[Union[int, float]] = None,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: small Minimal code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants