Repository navigation
feat(connections): SQL Server connection type - #1402
Conversation
|
Thanks for this. I ran the branch against a local SQL Server 2022 container (
I found three things I'd like fixed before merge. All three reproduced live. 1. Saving a connection through Edit breaks itHow it breaks:
The server doesn't catch it either: the "instanceName and port both set" warning tests truthiness (connection_config.ts:663). Suggested fix: treat an empty string as "not set" on both sides. Drop it in the Edit dialog the way Add does, and normalize 2. A certificate error answers as "database unreachable"The release note says a certificate the client can't verify is not read as unreachable. Live, with This is the real error chain under Bun:
Suggested fix: follow 3. The single-table lookup doesn't get the new statusesOn
The driver returns failures here as text with the error code dropped: Postgres has the same gap for a wrong password. The 0.9.7 release note lists it under "Not covered yet" (RELEASE_NOTES.md:266). To be consistent with that:
Smaller things, not blocking
|
|
I ran the same checks against SQL Server 2019 ( One more thing, confirmed on 2019: a schema with a non-ASCII name lists no tables.
|
5b9963d to
33eddc0
Compare
|
Thanks for testing this against real servers. All of it is addressed on the latest push.
The smaller items are in too: a port check at load, the Azure login caveat in the docs, and the read-only parity test across every connection type in both directions. |
jswir
left a comment
There was a problem hiding this comment.
Thanks, this all looks good. I re-reviewed the two new commits (94ccf1ab7, 33eddc0a1) and ran the touched specs: 356 pass across the five server files, and common.spec.ts in the SDK passes. I didn't re-run the live SQL Server checks this time.
- Edit with a custom port: fixed on both sides. I also checked that leaving a field out on Edit doesn't keep the old value:
mergeConnectionUpdatereplaces the wholesqlserverConnectionobject and only carries the password forward, so clearing the instance name in the Console still clears it. - Certificate error: fixed. The walks follow
originalError, and the test now asserts the outer error has no.cause, so it can't pass without walking the real chain. - Table lookup: fixed. A closed port answers 502 and a missing table 404. The refused-login gap is documented, as for Postgres.
- Non-ASCII schema: fixed with
N'…'in the SQL Server function only. - The smaller items (port check, Azure caveat, read-only parity test in both directions) are all in.
Three small things, none blocking:
- A refused login on the table lookup could become 424 later. The connector's text keeps SQL Server's error number, so
18456: Login failed…could map to rejected credentials. I haven't checked that the number survives for a login failure, so this is a follow-up, not a change for this PR. - Some TLS errors still read as unreachable. The new message pattern only excludes text containing "certificate", so a server that doesn't speak TLS ("wrong version number") still answers 502. Rare, and arguably fine.
- One line of the PR description is out of date. It says schema listing uses
sys.schemasjoined tosys.tables. The code now readsinformation_schema.tables, which also lists schemas that hold only views.
33eddc0 to
626c5bb
Compare
|
Thanks for the re-review.
The branch is also rebased onto current |
A connection of type sqlserver reaches Microsoft SQL Server through @malloydata/db-sqlserver: a SQL login over TCP with the connector's own field names, offered in the Console's connection dialogs, with schema and table listing, the password withheld on read, tedious errors classified as rejected credentials or an unreachable server, the experimental dialect accepted for every compile on the main and load-worker threads, and a colocated #@ persist on a SQL Server source refused at load with the new RefusedSource reason read_only_dialect. Signed-off-by: James Estes <james.estes@credibledata.com>
A request timeout is not an unreachable server: tedious puts ETIMEOUT on both, so only the ConnectionError reads as a connection failure. A rollup declaring a storage destination passes the read-only check, which is the colocated tier's question. Schemas are listed from information_schema.tables so one holding only views appears, as the column listing already includes them. The dialogs require a port only when no instance name locates the server. A test pins that every read-only dialect's connection type reports canPersist: false. Signed-off-by: James Estes <james.estes@credibledata.com>
…reads Signed-off-by: James Estes <james.estes@credibledata.com>
…e as unset The Console's edit form submits "" for an optional field left empty, and the connector drops the port whenever instanceName is defined at all, so an edit that never touched the field would send a connection on a non-default port to SQL Browser. The dialogs leave a blank one out of the payload and the server reads one as unset either way. Signed-off-by: James Estes <james.estes@credibledata.com>
… and port check The error walks follow mssql's originalError as well as cause, so a certificate the client cannot verify is no longer read as an unreachable server. The single-table lookup recognizes the SQL Server connector's failure text, answering 502 for an unreachable server and 404 for a missing table. Schema and table names are compared as N'' literals, so a non-ASCII name lists under a non-Unicode collation. A port that is not an integer from 1 to 65535 fails the config at load, the read-only parity check covers every connection type in both directions, and the docs say what is still not classified. Signed-off-by: James Estes <james.estes@credibledata.com>
Signed-off-by: James Estes <james.estes@credibledata.com>
626c5bb to
0bd04c8
Compare
Brings in #1402 (SQL Server connections). The only conflict was RELEASE_NOTES.md: main's file, with this branch's versions section above main's newest [Unreleased] section. errors.ts, server.ts, connection.controller.ts, api-doc.yaml and AGENTS.md merged cleanly, with the version errors and versionId threading kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3YLw4eQm8aoLsiS6ySHCC Signed-off-by: Sagar Swami Rao Kulkarni <sagarswamirao@gmail.com>
Two conflicts. errors.ts: main's SQL Server work (#1402) replaced `current.cause` with `wrappedError(current)` inside the two recognizer loops this branch had already folded into the shared `causeChain` walk, so the walk now steps through `wrappedError` and the stray assignments go. RELEASE_NOTES.md: both sides added an [Unreleased] section at the top; both kept, this branch's first. Signed-off-by: Sagar Swami Rao Kulkarni <sagarswamirao@gmail.com>
What
A connection of
type: "sqlserver"reaches Microsoft SQL Server through@malloydata/db-sqlserver(Malloy 0.0.435): a SQL login over TCP withserver,port,database,user,password,encrypt,trustServerCertificate,hostNameInCertificateandinstanceName. The field names are the connector's own, so the pojo mapping is a pass-through and the authentication kinds and connection-string form the connector also takes are additive later.connection.ts; static attributesdialectName: sqlserver,isPool: true,canStream: true,canPersist: false;validateConnectionShapechecks that aportis an integer from 1 to 65535 and warns when bothinstanceNameandportare set, since the driver takes the instance; a blankinstanceNameorhostNameInCertificateis read as unset, because the driver drops the port wheneverinstanceNameis defined at all; public view with the password withheld;sqlserverinDOUBLED_ESCAPE_DIALECTS(the connector runs withQUOTED_IDENTIFIER ON, T-SQL has no backslash escape).information_schema.tables, so a schema holding only views appears (dbothe default), and tables frominformation_schema.columns, addressedschema.table. Both compare names asN'…'literals at the SQL Server call sites, so a non-ASCII schema lists under a non-Unicode collation.experimental_dialects.tscallsacceptExperimental()for the experimental dialects this server bundles a connector for. It runs at startup inserver.tsand in the package-load worker, because the dialect registry is module state and each thread has its own. A model needs no##! experimental.dialect.sqlserverpragma; the connector's README carries the dialect's upstream status and limits.#@ persiston a SQL Server source is refused at load by the colocated gate with the newRefusedSource.reasonvalueread_only_dialect. The package loads, the source serves live, and the package's warnings carry the reason, the same shape as the other eligibility refusals. A source or rollup that declaresstorage=is not refused here; the storage build's passthrough check already names the source types it can read. A spec checks, across every type in the API'sConnection.typeenum and in both directions, that the read-only set and the staticcanPersistagree.ELOGIN(every server-side login refusal, with the server's message) answers as rejected credentials;ESOCKET,EINSTLOOKUPand a connect-timeoutETIMEOUTas unreachable. A request-timeoutETIMEOUTis not, since the server ran the query. The cause walks follow mssql'soriginalErroras well ascause, so anESOCKETcaused by a TLS verification failure is seen as one and stays unclassified: it answers as any unclassified driver failure on that route, and the log and the connection test name the certificate. On the single-table lookup, where the connector returns failures as text, an unreachable server answers 502 and a missing table 404; a refused login there is listed as not covered yet, as for Postgres.encryptandtrustServerCertificateas Yes/No selects, which meant teaching both dialogs' generic field renderer to honourselectOptions; andtypedConnectionConfig, which sendsportas an integer and the two switches as booleans, and leaves a blank optional field out rather than storing an empty string.docs/connections.md(fields, schema model, read-only status, and the two limits: every login refusal answers 424, including a transient Azure one, and the table-lookup gap), a paragraph on read-only connections indocs/materialization.md, the type lists in README and AGENTS, and an[Unreleased]release note.Testing
bun test: server 6808 pass, SDK 2121 pass, on the branch rebased onto currentmain.tsc,eslintand prettier clean on both packages.originalError, which carries Node's TLS error oncause); the certificate and table-lookup tests fail against the previous error walk.main: no new findings from themssql/tedioustree.mcr.microsoft.com/azure-sql-edge, collationSQL_Latin1_General_CP1_CI_AS), through the REST API on abun src/server.tsrun of this branch:flights -> by_carrierandorders -> by_customerreturn correct rows, the latter including a value with an embedded quote;/schemaslistsdbo,salesand a Cyrillic schemaПродажи, whose table lists only with theN'…'literal;POST /connections/testanswersok,Login failed for user 'sa'., a refused connect, andself signed certificatefor each case;CONNECTION_AUTH_FAILEDfor a wrong password and 502CONNECTION_FAILEDfor a closed port; the table lookup answers 502 for a closed port and 404 for a missing table;instanceNameandhostNameInCertificate, and setting a real instance name breaks it, which shows the edit reached the live connection;#@ persistsource appears inbuildPlan.refusedSourceswithreason: read_only_dialectand still answers a query live.