From 8bee1b96609f654db2b6ef126fa61467ca0077e9 Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Thu, 20 Mar 2025 14:16:35 -0700 Subject: [PATCH] More correct pg wire protocol error handling --- CHANGELOG.md | 1 + src/emulator/dataconnect/pgliteServer.ts | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c4e570440a..5b77215b8a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,3 +10,4 @@ - Updated the Firebase Data Connect local toolkit to v1.9.1, which adds support for generated Angular SDKs and updates Dart SDK fields to follow best practices. (#8340) - Fixed misleading comments in `firebase init dataconnect` `connector.yaml` template. - Improved Data Connect SQL permissions to better handle tables owned by IAM roles. (#8339) +- Fixed an issue where the Data Connect emulator would crash after some SQL errors. diff --git a/src/emulator/dataconnect/pgliteServer.ts b/src/emulator/dataconnect/pgliteServer.ts index f365ceb9cc0..c190d2b249c 100644 --- a/src/emulator/dataconnect/pgliteServer.ts +++ b/src/emulator/dataconnect/pgliteServer.ts @@ -164,6 +164,7 @@ export class PostgresServer { // TODO: Remove this code once https://github.com/electric-sql/pglite/pull/294 is released in PGLite export class PGliteExtendedQueryPatch { isExtendedQuery = false; + eqpErrored = false; constructor(public connection: PostgresConnection) {} @@ -184,6 +185,7 @@ export class PGliteExtendedQueryPatch { // 'Sync' indicates the end of an extended query if (message[0] === FrontendMessageCode.Sync) { this.isExtendedQuery = false; + this.eqpErrored = false; // Manually inject 'ReadyForQuery' message at the end return this.connection.createReadyForQuery(); @@ -191,9 +193,13 @@ export class PGliteExtendedQueryPatch { // A PGlite response can contain multiple messages for await (const message of getMessages(response)) { - // If a prepared statement leads to an error message, we need to end the pipeline. - if (message[0] === BackendMessageCode.ErrorMessage) { - this.isExtendedQuery = false; + // After an ErrorMessage in extended query protocol, we should throw away messages until the next Sync + // (per https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY:~:text=When%20an%20error,for%20each%20Sync.)) + if (this.eqpErrored) { + continue; + } + if (this.isExtendedQuery && message[0] === BackendMessageCode.ErrorMessage) { + this.eqpErrored = true; } // Filter out incorrect `ReadyForQuery` messages during the extended query protocol if (this.isExtendedQuery && message[0] === BackendMessageCode.ReadyForQuery) {