Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c355ada

Browse files
committed
chore: add tests for native
1 parent f78be28 commit c355ada

File tree

3 files changed

+118
-24
lines changed

3 files changed

+118
-24
lines changed

‎packages/pg/lib/native/client.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ var Client = (module.exports = function (config) {
2626
types: this._types,
2727
})
2828

29+
// Store maxResultSize configuration
30+
this._maxResultSize = config.maxResultSize
31+
2932
this._queryQueue = []
3033
this._ending = false
3134
this._connecting = false
@@ -100,6 +103,9 @@ Client.prototype._connect = function (cb) {
100103
// set internal states to connected
101104
self._connected = true
102105

106+
// Add a reference to the client for error bubbling
107+
self.native.connection = self;
108+
103109
// handle connection errors from the native layer
104110
self.native.on('error', function (err) {
105111
self._queryable = false
@@ -196,7 +202,7 @@ Client.prototype.query = function (config, values, callback) {
196202

197203
// we already returned an error,
198204
// just do nothing if query completes
199-
query.callback = () => {}
205+
query.callback = () => {}
200206

201207
// Remove from queue
202208
var index = this._queryQueue.indexOf(query)
@@ -289,14 +295,14 @@ Client.prototype._pulseQueryQueue = function (initialConnection) {
289295
// attempt to cancel an in-progress query
290296
Client.prototype.cancel = function (query) {
291297
if (this._activeQuery === query) {
292-
this.native.cancel(function () {})
298+
this.native.cancel(function () {})
293299
} else if (this._queryQueue.indexOf(query) !== -1) {
294300
this._queryQueue.splice(this._queryQueue.indexOf(query), 1)
295301
}
296302
}
297303

298-
Client.prototype.ref = function () {}
299-
Client.prototype.unref = function () {}
304+
Client.prototype.ref = function () {}
305+
Client.prototype.unref = function () {}
300306

301307
Client.prototype.setTypeParser = function (oid, format, parseFn) {
302308
return this._types.setTypeParser(oid, format, parseFn)

‎packages/pg/lib/native/query.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ NativeQuery.prototype.handleError = function (err) {
5656
err[normalizedFieldName] = fields[key]
5757
}
5858
}
59+
60+
// For maxResultSize exceeded errors, make sure we emit the error to the client too
61+
if (err.code === 'RESULT_SIZE_EXCEEDED') {
62+
if (this.native && this.native.connection) {
63+
// Need to emit the error on the client/connection level too
64+
process.nextTick(() => {
65+
this.native.connection.emit('error', err);
66+
});
67+
}
68+
}
69+
5970
if (this.callback) {
6071
this.callback(err)
6172
} else {
@@ -89,6 +100,9 @@ NativeQuery.prototype.submit = function (client) {
89100
this.native = client.native
90101
client.native.arrayMode = this._arrayMode
91102

103+
// Get the maxResultSize from the client if it's set
104+
this._maxResultSize = client._maxResultSize
105+
92106
var after = function (err, rows, results) {
93107
client.native.arrayMode = false
94108
setImmediate(function () {
@@ -100,6 +114,30 @@ NativeQuery.prototype.submit = function (client) {
100114
return self.handleError(err)
101115
}
102116

117+
// Check the result size if maxResultSize is configured
118+
if (self._maxResultSize) {
119+
// Calculate result size (rough approximation)
120+
let resultSize = 0;
121+
122+
// For multiple result sets
123+
if (results.length > 1) {
124+
for (let i = 0; i < rows.length; i++) {
125+
resultSize += self._calculateResultSize(rows[i]);
126+
}
127+
} else if (rows.length > 0) {
128+
resultSize = self._calculateResultSize(rows);
129+
}
130+
131+
// If the size limit is exceeded, generate an error
132+
if (resultSize > self._maxResultSize) {
133+
const error = new Error('Query result size exceeded the configured limit');
134+
error.code = 'RESULT_SIZE_EXCEEDED';
135+
error.resultSize = resultSize;
136+
error.maxResultSize = self._maxResultSize;
137+
return self.handleError(error);
138+
}
139+
}
140+
103141
// emit row events for each row in the result
104142
if (self._emitRowEvents) {
105143
if (results.length > 1) {
@@ -166,3 +204,59 @@ NativeQuery.prototype.submit = function (client) {
166204
client.native.query(this.text, after)
167205
}
168206
}
207+
208+
// Helper method to estimate the size of a result set
209+
NativeQuery.prototype._calculateResultSize = function (rows) {
210+
let size = 0;
211+
212+
// For empty results, return 0
213+
if (!rows || rows.length === 0) {
214+
return 0;
215+
}
216+
217+
// For array mode, calculate differently
218+
if (this._arrayMode) {
219+
// Just use a rough approximation based on number of rows
220+
return rows.length * 100;
221+
}
222+
223+
// For each row, approximate its size
224+
for (let i = 0; i < rows.length; i++) {
225+
const row = rows[i];
226+
227+
// Add base row size
228+
size += 24; // Overhead per row
229+
230+
// Add size of each column
231+
for (const key in row) {
232+
if (Object.prototype.hasOwnProperty.call(row, key)) {
233+
const value = row[key];
234+
235+
// Add key size
236+
size += key.length * 2; // Assume 2 bytes per character
237+
238+
// Add value size based on type
239+
if (value === null || value === undefined) {
240+
size += 8;
241+
} else if (typeof value === 'string') {
242+
size += value.length * 2; // Assume 2 bytes per character
243+
} else if (typeof value === 'number') {
244+
size += 8;
245+
} else if (typeof value === 'boolean') {
246+
size += 4;
247+
} else if (value instanceof Date) {
248+
size += 8;
249+
} else if (Buffer.isBuffer(value)) {
250+
size += value.length;
251+
} else if (Array.isArray(value)) {
252+
size += 16 + value.length * 8;
253+
} else {
254+
// For objects, use a rough estimate
255+
size += 32 + JSON.stringify(value).length * 2;
256+
}
257+
}
258+
}
259+
}
260+
261+
return size;
262+
}

‎packages/pg/test/integration/client/max-result-size-tests.js

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,19 @@ process.on('unhandledRejection', function (e) {
1010

1111
const suite = new helper.Suite()
1212

13-
// Test that result size exceeding maxResultSize properly raises an error
1413
suite.test('maxResultSize limit triggers error', (cb) => {
14+
// Check if we're running with the native client
15+
const isNative = helper.args.native
16+
console.log(isNative ? 'Testing with native client' : 'Testing with JavaScript client')
17+
1518
// Create a pool with a very small result size limit
1619
const pool = new pg.Pool({
1720
maxResultSize: 100, // Very small limit (100 bytes)
1821
...helper.args,
1922
})
2023

21-
// Track if we've seen the size exceeded error
2224
let sizeExceededErrorSeen = false
2325

24-
// Set up error handler on pool
2526
pool.on('error', (err) => {
2627
console.log('Pool error:', err.message, err.code)
2728
})
@@ -33,21 +34,20 @@ suite.test('maxResultSize limit triggers error', (cb) => {
3334
client.on('error', (err) => {
3435
console.log('Client error event:', err.message, err.code)
3536

36-
// If we get the expected error, mark it
37-
if (err.message === 'Query result size exceeded the configured limit'){
38-
assert.equal(err.code,'RESULT_SIZE_EXCEEDED','Error should have RESULT_SIZE_EXCEEDED code')
37+
// If we get any size exceeded error, mark it
38+
if (err.code === 'RESULT_SIZE_EXCEEDED'||
39+
err.message==='Query result size exceeded the configured limit'){
3940
sizeExceededErrorSeen = true
4041
}
4142
})
4243

43-
// Create a temp table
4444
return client
4545
.query('CREATE TEMP TABLE large_result_test(id SERIAL, data TEXT)')
4646
.then(() => {
47-
// Insert data that will exceed the size limit when selected
47+
// Insert rows that will exceed the size limit when queried
4848
const insertPromises = []
4949
for (let i = 0; i < 20; i++) {
50-
// Each row will have 50 bytes of data
50+
// Each row will have enough data to eventually exceed our limit
5151
const data = 'x'.repeat(50)
5252
insertPromises.push(client.query('INSERT INTO large_result_test(data) VALUES(1ドル)', [data]))
5353
}
@@ -56,7 +56,6 @@ suite.test('maxResultSize limit triggers error', (cb) => {
5656
.then(() => {
5757
console.log('Running query that should exceed size limit...')
5858

59-
// This query should fail due to exceeding size limit
6059
return client
6160
.query('SELECT * FROM large_result_test')
6261
.then(() => {
@@ -65,15 +64,14 @@ suite.test('maxResultSize limit triggers error', (cb) => {
6564
.catch((err) => {
6665
console.log('Query error caught:', err.message, err.code)
6766

68-
// The error should have the correct code
67+
// Both implementations should throw an error with this code
6968
assert.equal(err.code, 'RESULT_SIZE_EXCEEDED', 'Error should have RESULT_SIZE_EXCEEDED code')
7069

71-
// Give a little time for error events to be processed
70+
// Give time for error events to propagate
7271
return new Promise((resolve) => setTimeout(resolve, 100)).then(() => {
73-
// Verify we saw the expected error event
72+
// Verify we saw the error event
7473
assert(sizeExceededErrorSeen, 'Should have seen the size exceeded error event')
7574

76-
// Attempt cleanup but don't fail if it errors
7775
return client.query('DROP TABLE IF EXISTS large_result_test').catch(() => {
7876
/* ignore cleanup errors */
7977
})
@@ -85,16 +83,17 @@ suite.test('maxResultSize limit triggers error', (cb) => {
8583
pool.end(cb)
8684
})
8785
.catch((err) => {
86+
console.error('Test error:', err.message)
8887
client.release()
8988
pool.end(() => cb(err))
9089
})
9190
})
9291
.catch((err) => {
92+
console.error('Connection error:', err.message)
9393
pool.end(() => cb(err))
9494
})
9595
})
9696

97-
// Test that results under the maxResultSize limit work normally
9897
suite.test('results under maxResultSize limit work correctly', (cb) => {
9998
// Create a pool with a reasonably large limit
10099
const pool = new pg.Pool({
@@ -105,21 +104,16 @@ suite.test('results under maxResultSize limit work correctly', (cb) => {
105104
pool
106105
.connect()
107106
.then((client) => {
108-
// Create a temp table
109107
return client
110108
.query('CREATE TEMP TABLE small_result_test(id SERIAL, data TEXT)')
111109
.then(() => {
112-
// Insert a small amount of data
113110
return client.query('INSERT INTO small_result_test(data) VALUES(1ドル)', ['small_data'])
114111
})
115112
.then(() => {
116-
// This query should succeed
117113
return client.query('SELECT * FROM small_result_test').then((result) => {
118-
// Verify the result
119114
assert.equal(result.rows.length, 1, 'Should get 1 row')
120115
assert.equal(result.rows[0].data, 'small_data', 'Data should match')
121116

122-
// Clean up
123117
return client.query('DROP TABLE small_result_test')
124118
})
125119
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /