Files
fn_registry/python/functions/infra/mssql_query.md
T
egutierrez 86d68dc9f0 feat(infra): conexion y consulta directa a SQL Server (Navision) via pymssql
Grupo de capacidad nuevo 'sql-connect' (3 funciones) para conectar a un
Microsoft SQL Server (donde corre Navision) y consultar directamente, en
lugar del ida y vuelta manual de pegar CSVs.

- mssql_connect_py_infra: abre conexion pymssql (login_timeout acotado,
  credenciales por argumento, RuntimeError claro si falla).
- mssql_query_py_infra: SELECT parametrizada con binding seguro (sin
  inyeccion) sobre conexion abierta; devuelve {columns, rows, row_count};
  0 filas -> lista vacia; max_rows con fetchmany; read-only.
- run_mssql_query_py_pipelines: one-shot que compone connect+query y cierra
  siempre; CLI imprime JSON o CSV; contrasena desde env var (pass).

Pagina madre docs/capabilities/sql-connect.md + fila en INDEX.md.
Dependencia pymssql>=2.3.13 anadida a python/pyproject.toml + uv.lock.
Tests mock-based (11) verdes; error path verificado end-to-end contra el
driver real (host inalcanzable -> RuntimeError, acotado por login_timeout).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:29:49 +02:00

3.4 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, params, output, file_path
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path params output file_path
mssql_query function py infra 1.0.0 impure def mssql_query(conn, sql: str, params=None, max_rows: int | None = None) -> dict Ejecuta una SELECT parametrizada (binding seguro de pymssql, sin inyeccion) sobre una conexion SQL Server/Navision ya abierta y devuelve {columns, rows como lista de dicts, row_count}. Opcion max_rows para limitar las filas.
mssql
sqlserver
navision
sql-connect
infra
false error_go_core
true
test_golden_maps_rows_to_dicts
test_binding_passes_params_to_driver
test_zero_rows_no_error
test_max_rows_uses_fetchmany
test_description_none_empty_columns
test_execution_error_raises_runtimeerror
python/functions/infra/mssql_query_test.py
name desc
conn Conexion abierta (la que devuelve mssql_connect). No se abre ni cierra aqui; se reutiliza por duck typing via conn.cursor().
name desc
sql Sentencia SELECT con placeholders pymssql %s (posicional) o %(nombre)s (nombrado) para los valores a vincular.
name desc
params Tuple/list para placeholders posicionales, dict para nombrados, o None. Se pasa a cursor.execute(sql, params) para binding seguro del driver (nunca interpolacion).
name desc
max_rows Si es int>0, limita a las primeras max_rows filas (fetchmany). Si None, devuelve todas (fetchall).
Dict con tres claves: 'columns' (lista de nombres de columna en orden, vacia si no hubo result set), 'rows' (lista de dicts columna->valor, una por fila), 'row_count' (int len(rows)). python/functions/infra/mssql_query.py

Ejemplo

import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from infra.mssql_connect import mssql_connect
from infra.mssql_query import mssql_query

conn = mssql_connect(
    host="10.0.0.5", database="navdb", user="readonly", password="<desde pass>"
)
try:
    res = mssql_query(
        conn,
        "SELECT TOP 10 No_, Amount FROM [dbo].[Cartera] WHERE [Customer No_] = %s",
        ("CLI-0001",),
    )
    print(res["columns"])      # ['No_', 'Amount']
    print(res["row_count"])    # numero de filas devueltas
    for fila in res["rows"]:
        print(fila["No_"], fila["Amount"])
finally:
    conn.close()

Cuando usarla

Cuando ya tienes una conexion abierta con mssql_connect y quieres iterar consultas SELECT sobre Navision / SQL Server sin reabrir la conexion en cada una. Pasa los valores variables como params para que el driver los vincule de forma segura (sin inyeccion) en lugar de construir el SQL con f-strings.

Gotchas

  • Los placeholders de pymssql son %s (posicional) y %(nombre)s (nombrado), NO el ? de pyodbc. Si usas el placeholder equivocado, el binding falla.
  • Pasa los valores SIEMPRE por el argumento params, jamas con f-string o % dentro del SQL: interpolar abre la puerta a inyeccion SQL.
  • No hace commit: es read-only, pensada para SELECT.
  • No cierra la conexion — la gestiona el caller (abrir una vez, consultar muchas, cerrar al final).
  • max_rows usa cursor.fetchmany(max_rows); con None usa fetchall().
  • Si la sentencia no produce result set (cursor.description is None), columns y rows vuelven como listas vacias en lugar de fallar.
  • El mensaje de error es generico a proposito: no incluye el SQL ni los params para no filtrar datos sensibles.