The reference application in examples/NetBurner_MSSQL_Client/ demonstrates production-style use of TDSLite: persistent configuration, catalog browsing, safe writes, and a multipage web UI.
| File | Responsibility |
|---|---|
main.cpp |
Boot, HTTP start, remote console, runtime init/poll loop |
web.cpp |
CPPCALL renderers, POST handlers, SQL generation, client script injection |
sql_runtime.h / sql_runtime.cpp |
Job queue, driver lifecycle, query/mutation execution |
sql_nv.h / sql_nv.cpp |
AppData persistence for connection and query preferences |
sql_catalog.h / sql_catalog.cpp |
Database/table/column name caches |
sql_results.h / sql_results.cpp |
Last result storage, CSV export data |
html/*.html |
Static page shells with <!--CPPCALL ...--> placeholders |
html/style.css |
Shared layout and form styling |
src/htmldata.cpp |
Generated — embedded web assets (do not edit by hand) |
sql_runtime_config (in sql_runtime.h) holds:
- Connection: server, port, user, password, database
- Browse context: table name, selected columns (comma-separated)
- Query options: custom SQL text, max rows, filter expression, sort column/direction
- Write context: working table for Insert/Update page
Defaults are applied in SqlRuntimeInit(). NV-loaded values override defaults when present.
HTTP handlers call SqlRuntimeRequest*() functions; the worker task processes one job at a time:
| Request function | Trigger | Behavior |
|---|---|---|
SqlRuntimeRequestTestConnection() |
Configure → Test Connection | Login verify; does not save NV |
SqlRuntimeRequestRun() |
Browse → Run Query | Read-only SELECT (builder or custom) |
SqlRuntimeRequestBrowseDatabases() |
Configure → Refresh Databases | Fills database catalog |
SqlRuntimeRequestBrowseTables() |
Browse/Write → Refresh Tables | Table list for current DB |
SqlRuntimeRequestBrowseColumns() |
Browse/Write → Refresh Columns | Column metadata for selected table |
SqlRuntimeRequestExecutePendingMutation() |
Write → Confirm | Runs staged INSERT/UPDATE/custom write |
SqlRuntimePoll() advances the worker from main.cpp's loop. POST handlers update config, call a Request* function, and redirect or re-render the page.
- Load config (RAM + NV merge)
driver.connect(params)with current credentials- Execute SQL or catalog queries
- Populate
sql_result_storeor catalogs driver.disconnect()- Update
sql_connection_status(idle / busy / last error)
Settings live under AppData branch SqlSettings via config_obj:
- Saved only when user clicks Save Settings after successful validation
- Password field blank on Configure form = keep existing stored password
SqlNvClear()removes the branch (factory reset pattern)SqlNvLastVerifiedSecs()records last successful connection test
Implementation detail: saving must clear the fConfigHidden branch flag so NetBurner writes NV flash — see patch notes in PATCHES.md if you fork this module.
Catalogs are plain C string arrays with counts:
SqlCatalogGetDatabases() // up to 64 names
SqlCatalogGetTables()
SqlCatalogGetColumns()Invalidation happens when:
- Database name changes
- Table name changes
- Explicit refresh job completes
Catalogs are not persisted to NV — they are refreshed from SQL Server on demand.
After each query or mutation:
- Column headers and cell text (HTML-escaped at render time in
web.cpp) - Row count, duration ms, success flag, user-facing message
SqlResultsIsMutation()distinguishes DML from SELECT for Results page layoutSqlResultsExportCsv()builds RFC-style CSV for/export.csv
Maximum rows returned to the module are capped (default 250) regardless of SQL Server result size.
- Builder mode generates
SELECT TOP (N) [cols] FROM [db].[dbo].[table] WHERE ... ORDER BY ... - Custom mode accepts user SQL but must pass read-only validation (starts with
SELECT, no forbidden keywords) - Identifiers are bracket-quoted; filter/sort inputs are validated
Three paths on Insert / Update:
- Guided Insert — form fields → parameterized-style INSERT preview
- Guided Update — key column + SET fields → UPDATE with WHERE on key
- Custom Write SQL — user text, validated separately from SELECT rules
All writes go through staging:
- POST builds SQL and stores in pending mutation state
- User sees preview on the same page (live JS preview + server pending panel)
- Confirm and Execute enqueues mutation job; Cancel clears pending state
No write runs on preview POST alone.
The Write page requires a table selected from the browsed dropdown (setwritetable POST). Manual table name entry was removed to avoid typos on DML. Load Columns / auto-init refresh column metadata for form fields.
StartHttp()inmain.cppregisters the default NetBurner web server- POST handlers register at static init time via global
HtmlPostVariableListCallbackobjects inweb.cpp(no separate web init call frommain.cpp) - Pages use shared header/footer/nav rendered by CPPCALL helpers (
ShowHeaderStatus,ShowQueryStatus, etc.)
See Web UI for page-by-page behavior and comphtml rules.
Common extension points:
| Goal | Where to change |
|---|---|
| Add a new page | New html/foo.html, CPPCALLs in web.cpp, register in makefile HTML list |
| New SQL operation | New job enum in sql_runtime.h, handler in sql_runtime.cpp, POST in web.cpp |
| Different NV layout | sql_nv.cpp field names / branch structure |
| Stricter write policy | Validation functions in web.cpp before staging |
Keep SQL work off the HTTP thread — always enqueue through SqlRuntime.
Do not commit:
obj/— object files and binariessrc/htmldata.cpp— regenerated by comphtml
See Build system.