Skip to content

How SQL Server, PostgreSQL & MySQL report errors

You’ve spent five modules reading SQL Server logs. The method holds on every engine — locate the phase, read the artifact, name the failure — but the reading changes. The same broken deploy lands on PostgreSQL, and the error isn’t where you’d reach for it. It’s a different door.

Hey folks. I’m Forge Barrett, master of the Content Forge here at SchemaSmith.

This is the finale of Course 8. One last skill — reading the three engines’ dialects — and then we write the thing this whole course was building toward: your team’s diagnostic runbook. Let’s close it out.

SchemaSmith runs the same convergence engine on all three databases. But each database hands progress and errors back through its own mechanism:

  • SQL Server raises InfoMessage events. Real errors (severity above 10) land in both the progress log and the errors log — the errors log entry carries the message and the line number.
  • PostgreSQL streams Notice messages. The SQLSTATE prints right in the progress log; the errors log stays empty.
  • MySQL has no async message event at all. It runs the whole deploy on one connection, so it can’t push a notice mid-work — instead the engine writes progress to a SchemaSmith_StatusMessages table and a separate connection polls it every couple hundred milliseconds.

Same engine, three doors. Let’s watch one failure come through all of them.

MariaDB comes through the MySQL door — same client path, same SchemaSmith_StatusMessages table polled on a second connection. It’s a separate platform selection with its own native DDL, but its error-reporting mechanism is the MySQL one. Four platforms, three doors.

The lab seeds two customers sharing an email, then flips IX_Customer_Email to unique. It fails at the index phase on every engine — the same failure. Read the progress log and the errors log on each:

EngineProgress.logErrors.log
SQL ServerThe CREATE UNIQUE INDEX statement terminated… duplicate key… (ana.f@shop.test)populated — same message + at Line: 2
PostgreSQL23505: could not create unique index "ix_customer_email"empty
MySQLDuplicate entry 'ana.f@shop.test' for key 'Customer.IX_Customer_Email'empty

There’s the headline: only SQL Server populates the errors log. On PostgreSQL and MySQL, the fault detail — SQLSTATE and all — is in the progress log, and the errors log is empty. Go looking in the wrong file on the wrong engine and you’ll swear the tool told you nothing. It told you plenty; it just used a different door.

And the code is different every time: 1505 on SQL Server (it prints the message, not the number), 23505 on PostgreSQL (SQLSTATE, printed literally), 1062 on MySQL. One failure, three codes. That’s what the runbook’s lookup table is for.

SQL Server scripts are chatty — PRINT lines, low-severity warnings. SchemaSmith mutes that chatter by default so the log stays readable, and gives you one dial to unmute it: VerboseLogging (SQL-Server-only). The lab’s verbose package adds a script that PRINTs a line every deploy. Run it plain, then run it with the dial up:

schemaquench --ConfigFile:quench.settings.verbose.json --LogPath:"$PWD/logs-plain"
schemaquench --ConfigFile:quench.settings.verbose.json --LogPath:"$PWD/logs-verbose" --VerboseLogging=true

On SQL Server the PRINT line is gone from the plain log and there in the verbose one — your script’s output, suppressed by default, brought back on demand. On PostgreSQL a RAISE NOTICE shows either way (the dial’s a no-op there — Postgres surfaces its notices by default). On MySQL the line shows up nowhere: your scripts have no progress channel at all, so there’s nothing to unmute. One dial, one engine, one job — and it only touches your output; SchemaSmith’s own phase progress always shows.

Good deal — but mind the syntax. Set it with the = form, --VerboseLogging=true, or in your settings file. The colon form (--VerboseLogging:true) is silently ignored for config settings — a footgun worth knowing before it bites you.

Here’s the finale. Every module gave you a piece; now they become one page you keep.

Open runbook.md in the lab. It’s a fill-in team diagnostic runbook, and it pulls the whole course together:

  • Open Failures.log first — who failed, why, and which artifact to open, without hunting through the full progress trail. Progress.log below is the deeper core loop.
  • The phase map — the order the engine runs, so you know what “the index phase” comes after.
  • The core loop — locate the phase, read the artifact, pick the recovery.
  • Whose problem is it — the per-item line that tells a mechanical failure (FAILED to quench:) from your script (Unable to quench '<path>') from your data (Error delivering <table>).
  • Where to look, per engine — the door table you just learned.
  • The error codes — one failure, three codes, across all three engines.
  • The recovery decision tree — plain re-run, --ResumeQuench, or mark-done.
  • Your incidents — a blank log that becomes your fastest section over time.

Copy it into your own repo. That’s the deliverable — a one-page answer to “the deploy failed, now what?” that works on any engine your shop runs.

Check yourself: A deploy fails on PostgreSQL. You open the errors log to read the fault — and it's empty. Did the tool fail to report the error?

No — it reported it through PostgreSQL’s door. On PostgreSQL (and MySQL), the fault detail lives in the progress log, and the errors log stays empty; only SQL Server populates the errors log (with the message and line number). PostgreSQL prints the SQLSTATE — 23505, 23503, 23502 and the like — right in the progress log. So the fix is simply to read the right file: Progress.log on PostgreSQL and MySQL, Errors.log (or either) on SQL Server. Same failure, different door — which is exactly why the runbook’s per-engine “where to look” table earns its place.

That’s the course. You can walk into any failed deploy — on SQL Server, PostgreSQL, or MySQL — locate the phase from the progress log, read the artifact the engine left behind, name whose problem it is, and pick the recovery that fits. And now you’ve got a runbook so the next person doesn’t start from scratch. For the lookup tables on their own, the SchemaSmith reference docs carry a per-platform error-codes and reporting-channels page.

Six modules, one method, three engines. You came in reading a stack trace and hoping; you leave reading a phase map and knowing.


The forge doesn’t hide its work. Every engine has a different door, but behind all of them the same fire is doing the same shaping — and now you can read any of them by the light it throws. Take the runbook, keep it close, and let it grow with every incident your team survives together.

Got a runbook you’ve built from this course, or a failure that stumped it? Email me at forgebarrett@schemasmith.com — I read every one.

That’s Course 8. Thanks for forging through the whole thing with me.

Until then, may your doors open easy and your runbook never need its last page.

— Forge