Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mssql_python/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2623,6 +2623,8 @@ def _bulkcopy(
pycore_connection = mssql_py_core.PyCoreConnection(pycore_context)
pycore_cursor = pycore_connection.cursor()

# Call bulkcopy with explicit keyword arguments
# The API signature: bulkcopy(table_name, data_source, batch_size=0, timeout=30, ...)
result = pycore_cursor.bulkcopy(
table_name,
iter(data),
Expand All @@ -2635,6 +2637,7 @@ def _bulkcopy(
keep_nulls=keep_nulls,
fire_triggers=fire_triggers,
use_internal_transaction=use_internal_transaction,
python_logger=logger, # Pass Python logger handle to pycore
)

return result
Expand Down
56 changes: 48 additions & 8 deletions mssql_python/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,20 @@ def _setup_handlers(self):
# Custom formatter to extract source from message and format as CSV
class CSVFormatter(logging.Formatter):
def format(self, record):
# Extract source from message (e.g., [Python] or [DDBC])
msg = record.getMessage()
if msg.startswith("[") and "]" in msg:
end_bracket = msg.index("]")
source = msg[1:end_bracket]
message = msg[end_bracket + 2 :].strip() # Skip '] '
# Check if this is from Rust (via rust_log method)
if hasattr(record, 'funcName') and record.funcName == "rust":
source = "Rust"
message = record.getMessage()
else:
source = "Unknown"
message = msg
# Extract source from message (e.g., [Python] or [DDBC])
msg = record.getMessage()
if msg.startswith("[") and "]" in msg:
end_bracket = msg.index("]")
source = msg[1:end_bracket]
message = msg[end_bracket + 2 :].strip() # Skip '] '
else:
source = "Unknown"
message = msg

# Format timestamp with milliseconds using period separator
timestamp = self.formatTime(record, "%Y-%m-%d %H:%M:%S")
Expand Down Expand Up @@ -326,6 +331,41 @@ def _write_log_header(self):
pass # Even stderr notification failed
# Don't crash - logging continues without header

def rust_log(self, level: int, msg: str, filename: str = "cursor.rs", lineno: int = 0):
"""
Logging method for Rust code with custom source location.

Args:
level: Log level (DEBUG, INFO, WARNING, ERROR)
msg: Message string (already formatted)
filename: Source filename (e.g., 'cursor.rs')
lineno: Line number in source file
"""
try:
if not self._logger.isEnabledFor(level):
return

# Create a custom LogRecord with Rust source location
import logging as log_module
record = log_module.LogRecord(
name=self._logger.name,
level=level,
pathname=filename,
lineno=lineno,
msg=msg,
args=(),
exc_info=None,
func="rust",
sinfo=None
)
self._logger.handle(record)
except Exception:
# Fallback - use regular logging
try:
self._logger.log(level, msg)
except:
pass

def _log(self, level: int, msg: str, add_prefix: bool = True, *args, **kwargs):
"""
Internal logging method with exception safety.
Expand Down
Loading