Skip to main content

The direct MSSQL path

Audience: developers & AI agents · Scope: BcHelper::getInforDb() and the reporting queries that use it · Last reviewed: 2026-07-20

TL;DR — For anything report-shaped — costing, schedules, saved SQL exports — the site talks straight to Infor's SQL Server database over PDO/sqlsrv, bypassing the IDO layer entirely. The connection is reused within a process, health-checked before reuse, and reconnected with bounded retries on transient errors. It depends on the site-to-site VPN being up.

Implementation: data/customization/system_overrides/BcHelper.php, getInforDb().

Connecting

$dsn = 'sqlsrv:Server=<infor_db_ip_address>\<infor_db_instance_name>[,<infor_db_port>]'
. ';Database=<infor_db_database_name>'
. ';TrustServerCertificate=yes;Encrypt=no;LoginTimeout=<wait>';

After connecting, the session is put into the right Infor site context:

SetSiteSp 'BETACALC', NULL

Do not skip that call in new code paths — without it queries run against the wrong site context.

PDO::ATTR_ERRMODE is ERRMODE_EXCEPTION, so failures throw rather than returning false.

TrustServerCertificate=yes + Encrypt=no means the transport is not verified or encrypted at the TLS layer — the confidentiality guarantee comes from the VPN, not from the SQL connection. Worth knowing before anyone moves this off the VPN.

Connection reuse and retry

  • The PDO handle is kept in a static (self::$inforDb) and reused for the life of the process.

  • Before reuse it is health-checked; if the check throws, the handle is dropped and a reconnect happens. This matters because a long-running CLI script will otherwise hold a connection that the server or the VPN has already dropped.

  • Reconnection loops up to $maxAttempts, and only retries transient SQLSTATEs:

    SQLSTATERoughly
    08S01, 08S02communication link failure
    HYT00, HYT01timeout expired
    40001deadlock / serialization failure
    40501service busy / throttled
    40613database unavailable
  • Anything else — bad credentials above all — fails immediately rather than burning the retry budget.

  • A post-connect SELECT 1 confirms the new handle before it is cached.

Who uses it

ConsumerWhat for
models/bccostingsheet.phpMargin Analysis / costing sheet
models/sqlexports.phpthe Infor App DB endpoint of the SQL → Google Sheet exports
models/bcproductionschedule.php, bcfabricationschedule.php, bcassemblyschedule.php, bcwarehousekittingdata.php, bckittingpicklist.phpthe schedule exports

Most of these run on a schedule — see Scheduled jobs.

Logging & monitoring

Log typeWhat lands there
custom_infor_pdoconnection attempts, the SQLSTATE and message of each failure, health-check failures and reconnects

There are no CloudWatch metrics on this path, unlike the REST API. A silently failing MSSQL job shows up only in the log and in stale output, so don't assume an alert exists. (The costing sheet is the exception — it emits its own Margin-Analysis-Tool failure metric.)

Gotchas & caveats

  • The VPN is a hard dependency. When it drops, every consumer on this path fails while the REST path keeps working — which makes "Infor is down" reports ambiguous. Check which path the feature uses.
  • Retries only help with blips. A downed VPN produces the same transient SQLSTATEs, so the retry loop just delays the inevitable failure; don't raise $maxAttempts hoping to ride it out.
  • pdo_sqlsrv must be installed. It is a native extension, so a PHP upgrade can silently drop it. The local setup story (MSSQL via PDO, the OpenSSL/root-cert wrinkles) is written up in the off-git notes/Infor DB/ folder rather than here; see also environment.md.
  • These are reporting queries against a live ERP. They are read-mostly by convention, not by permission — be deliberate about anything that writes.
  • No query timeout is set beyond the login timeout. A pathological query can hang a CLI run.