@@ -16,10 +16,12 @@ import {
16
16
PostgresConnection ,
17
17
type PostgresConnectionOptions ,
18
18
FrontendMessageCode ,
19
+ BackendMessageCode ,
19
20
} from "pg-gateway" ;
20
21
import { logger } from "../../logger" ;
21
22
import { hasMessage , FirebaseError } from "../../error" ;
22
23
import { moveAll } from "../../fsutils" ;
24
+ import { StringDecoder } from "node:string_decoder" ;
23
25
24
26
export const TRUNCATE_TABLES_SQL = `
25
27
DO $do$
34
36
END
35
37
$do$;` ;
36
38
39
+ const decoder = new StringDecoder ( ) ;
40
+ const pgliteDebugLog = fs . createWriteStream ( "pglite-debug.log" ) ;
41
+
37
42
export class PostgresServer {
38
43
private baseDataDirectory ?: string ;
39
44
private importPath ?: string ;
@@ -46,7 +51,7 @@ export class PostgresServer {
46
51
const getDb = this . getDb . bind ( this ) ;
47
52
48
53
const server = net . createServer ( async ( socket ) => {
49
- await fromNodeSocket ( socket , {
54
+ const connection = await fromNodeSocket ( socket , {
50
55
serverVersion : "17.4 (PGlite 0.3.3)" ,
51
56
auth : { method : "trust" } ,
52
57
@@ -62,13 +67,16 @@ export class PostgresServer {
62
67
await db . query ( "DEALLOCATE ALL" ) ;
63
68
}
64
69
const response = await db . execProtocolRaw ( data ) ;
65
-
66
- for await ( const message of getMessages ( response ) ) {
70
+ for await ( const message of extendedQueryPatch . filterResponse ( data , response ) ) {
67
71
yield message ;
68
72
}
73
+
74
+ // Extended query patch removes the extra Ready for Query messages that
75
+ // pglite wrongly sends.
69
76
} ,
70
77
} ) ;
71
78
79
+ const extendedQueryPatch : PGliteExtendedQueryPatch = new PGliteExtendedQueryPatch ( connection ) ;
72
80
socket . on ( "end" , ( ) => {
73
81
logger . debug ( "Postgres client disconnected" ) ;
74
82
} ) ;
@@ -247,7 +255,7 @@ export class PostgresServer {
247
255
constructor ( args : { dataDirectory ?: string ; importPath ?: string ; debug ?: boolean } ) {
248
256
this . baseDataDirectory = args . dataDirectory ;
249
257
this . importPath = args . importPath ;
250
- this . debug = args . debug ? 5 : 0 ;
258
+ this . debug = args . debug ? 1 : 0 ;
251
259
}
252
260
}
253
261
@@ -271,3 +279,51 @@ export async function fromNodeSocket(socket: net.Socket, options?: PostgresConne
271
279
272
280
return new PostgresConnection ( { readable : rs , writable : ws } , opts ) ;
273
281
}
282
+
283
+ export class PGliteExtendedQueryPatch {
284
+ isExtendedQuery = false ;
285
+ eqpErrored = false ;
286
+
287
+ constructor ( public connection : PostgresConnection ) { }
288
+
289
+ async * filterResponse ( message : Uint8Array , response : Uint8Array ) {
290
+ // 'Parse' indicates the start of an extended query
291
+ const pipelineStartMessages : number [ ] = [
292
+ FrontendMessageCode . Parse ,
293
+ FrontendMessageCode . Bind ,
294
+ FrontendMessageCode . Close ,
295
+ ] ;
296
+ const decoded = decoder . write ( message as any as Buffer ) ;
297
+
298
+ pgliteDebugLog . write ( "Front: " + decoded ) ;
299
+
300
+ if ( pipelineStartMessages . includes ( message [ 0 ] ) ) {
301
+ this . isExtendedQuery = true ;
302
+ }
303
+
304
+ // 'Sync' indicates the end of an extended query
305
+ if ( message [ 0 ] === FrontendMessageCode . Sync ) {
306
+ this . isExtendedQuery = false ;
307
+ this . eqpErrored = false ;
308
+ }
309
+
310
+ // A PGlite response can contain multiple messages
311
+ for await ( const bm of getMessages ( response ) ) {
312
+ // After an ErrorMessage in extended query protocol, we should throw away messages until the next Sync
313
+ // (per https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY:~:text=When%20an%20error,for%20each%20Sync.))
314
+ if ( this . eqpErrored ) {
315
+ continue ;
316
+ }
317
+ if ( this . isExtendedQuery && bm [ 0 ] === BackendMessageCode . ErrorMessage ) {
318
+ this . eqpErrored = true ;
319
+ }
320
+ // Filter out incorrect `ReadyForQuery` messages during the extended query protocol
321
+ if ( this . isExtendedQuery && bm [ 0 ] === BackendMessageCode . ReadyForQuery ) {
322
+ pgliteDebugLog . write ( "Filtered: " + decoder . write ( bm as any as Buffer ) ) ;
323
+ continue ;
324
+ }
325
+ pgliteDebugLog . write ( "Sent: " + decoder . write ( bm as any as Buffer ) ) ;
326
+ yield bm ;
327
+ }
328
+ }
329
+ }
0 commit comments