@@ -4,23 +4,19 @@ var types = require('pg-types')
44
55var matchRegexp = / ^ ( [ A - Z a - z ] + ) (?: ( \d + ) ) ? (?: ( \d + ) ) ? /
66
7+ let parserCache = new Map ( )
8+
79// result object returned from query
810// in the 'end' event and also
911// passed as second argument to provided callback
1012class Result {
11- constructor ( rowMode , types ) {
13+ constructor ( _rowMode , types ) {
1214 this . command = null
1315 this . rowCount = null
1416 this . oid = null
1517 this . rows = [ ]
1618 this . fields = [ ]
17- this . _parsers = undefined
1819 this . _types = types
19- const rowAsArray = rowMode === 'array'
20- if ( rowAsArray ) {
21- this . parseRow = this . _parseRowAsArray
22- }
23- this . _prebuiltEmptyResultObject = null
2420 }
2521
2622 clear ( ) {
@@ -29,82 +25,69 @@ class Result {
2925 this . oid = null
3026 this . rows = [ ]
3127 this . fields = [ ]
32- this . _parsers = undefined
3328 }
3429
3530 // adds a command complete message
3631 addCommandComplete ( msg ) {
37- var match
38- if ( msg . text ) {
39- // pure javascript
40- match = matchRegexp . exec ( msg . text )
41- } else {
42- // native bindings
43- match = matchRegexp . exec ( msg . command )
44- }
45- if ( match ) {
46- this . command = match [ 1 ]
47- if ( match [ 3 ] ) {
48- // COMMMAND OID ROWS
49- this . oid = parseInt ( match [ 2 ] , 10 )
50- this . rowCount = parseInt ( match [ 3 ] , 10 )
51- } else if ( match [ 2 ] ) {
52- // COMMAND ROWS
53- this . rowCount = parseInt ( match [ 2 ] , 10 )
54- }
55- }
56- }
57-
58- _parseRowAsArray ( rowData ) {
59- var row = new Array ( rowData . length )
60- for ( var i = 0 , len = rowData . length ; i < len ; i ++ ) {
61- var rawValue = rowData [ i ]
62- if ( rawValue !== null ) {
63- row [ i ] = this . _parsers [ i ] ( rawValue )
64- } else {
65- row [ i ] = null
66- }
32+ var match = matchRegexp . exec ( msg . text ?? msg . command )
33+ if ( ! match ) return
34+ this . command = match [ 1 ]
35+ if ( match [ 3 ] ) {
36+ // COMMMAND OID ROWS
37+ this . oid = parseInt ( match [ 2 ] , 10 )
38+ this . rowCount = parseInt ( match [ 3 ] , 10 )
39+ } else if ( match [ 2 ] ) {
40+ // COMMAND ROWS
41+ this . rowCount = parseInt ( match [ 2 ] , 10 )
6742 }
68- return row
69- }
70-
71- parseRow ( rowData ) {
72- let row = { ...this . _prebuiltEmptyResultObject }
73- for ( let i = 0 , len = rowData . length ; i < len ; i ++ ) {
74- let rawValue = rowData [ i ]
75- if ( rawValue === null ) continue
76- row [ this . fields [ i ] . name ] = this . _parsers [ i ] ( rawValue )
77- }
78- return row
7943 }
8044
8145 addRow ( row ) {
8246 this . rows . push ( row )
8347 }
8448
49+ static _p ( p , v ) {
50+ return v === null ? null : p ( v )
51+ }
52+
8553 addFields ( fieldDescriptions ) {
8654 // clears field definitions
8755 // multiple query statements in 1 action can result in multiple sets
8856 // of rowDescriptions...eg: 'select NOW(); select 1::int;'
8957 // you need to reset the fields
9058 this . fields = fieldDescriptions
91- if ( this . fields . length ) {
92- this . _parsers = new Array ( fieldDescriptions . length )
93- }
9459
95- var row = { }
60+ let localTypes = this . _types || types
9661
97- for ( var i = 0 ; i < fieldDescriptions . length ; i ++ ) {
98- var desc = fieldDescriptions [ i ]
99- row [ desc . name ] = null
62+ let parseFn
63+ const cacheKey = fieldDescriptions . map ( desc => desc . dataTypeID + "|" + desc . name ) . join ( ',' )
64+ parseFn = parserCache . get ( cacheKey )
65+ if ( ! parseFn ) {
66+ parseFn = 'return function(rowData){return {'
67+ let args = [ ] , args2 = [ ]
68+ for ( let i = 0 ; i < fieldDescriptions . length ; i ++ ) {
69+ let desc = fieldDescriptions [ i ]
70+
71+ const parser = localTypes . getTypeParser ( desc . dataTypeID , desc . format || 'text' )
72+ if ( parser === String ) {
73+ parseFn += `${ JSON . stringify ( desc . name ) } : rowData[${ i } ],`
74+ } else {
75+ parseFn += `${ JSON . stringify ( desc . name ) } : _p(a${ i } ,rowData[${ i } ]),`
76+ args . push ( 'a' + i )
77+ args2 . push ( parser )
78+ }
79+ }
10080
101- if ( this . _types ) {
102- this . _parsers [ i ] = this . _types . getTypeParser ( desc . dataTypeID , desc . format || 'text' )
103- } else {
104- this . _parsers [ i ] = types . getTypeParser ( desc . dataTypeID , desc . format || 'text' )
81+ parseFn += '}}'
82+ parseFn = new Function ( '_p' , ...args , parseFn )
83+ parseFn = parseFn ( Result . _p , ...args2 )
84+ if ( parserCache . size > 256 ) {
85+ // prevent unbounded memory growth
86+ parserCache . clear ( )
10587 }
88+ parserCache . set ( cacheKey , parseFn )
10689 }
107- this . _prebuiltEmptyResultObject = row
90+ this . parseRow = parseFn
10891 }
10992}
11093
0 commit comments