• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knex / knex / 22079248648

16 Feb 2026 10:24PM UTC coverage: 93.125% (-0.03%) from 93.158%
22079248648

push

github

web-flow
Postgres: clearer error when pg-query-stream is missing (#6362)

---------

Co-authored-by: Tony Mobily <tonymobily@gmail.com>

3733 of 4410 branches covered (84.65%)

Branch coverage included in aggregate %.

56 of 65 new or added lines in 2 files covered. (86.15%)

15568 of 16316 relevant lines covered (95.42%)

2277.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.43
/test/unit/dialects/postgres.js
1
const knex = require('../../../knex');
1✔
2
const expect = require('chai').expect;
1✔
3
const sinon = require('sinon');
1✔
4
const pgDialect = require('../../../lib/dialects/postgres/index.js');
1✔
5
const _ = require('lodash');
1✔
6
const { isFunction } = require('../../../lib/util/is');
1✔
7

8
describe('Postgres Unit Tests', function () {
1✔
9
  let checkVersionStub, querySpy;
10
  before(() => {
1✔
11
    const fakeConnection = {
1✔
12
      query: (...args) => {
13
        const cb = args.find((arg) => {
3✔
14
          return isFunction(arg);
6✔
15
        });
16
        cb();
3✔
17
      },
18
      on: _.noop,
19
    };
20
    querySpy = sinon.spy(fakeConnection, 'query');
1✔
21

22
    checkVersionStub = sinon
1✔
23
      .stub(pgDialect.prototype, 'checkVersion')
24
      .callsFake(function () {
25
        return Promise.resolve('9.6');
2✔
26
      });
27
    sinon
1✔
28
      .stub(pgDialect.prototype, '_acquireOnlyConnection')
29
      .returns(Promise.resolve(fakeConnection));
30
  });
31
  afterEach(() => {
1✔
32
    querySpy.resetHistory();
8✔
33
  });
34
  after(() => {
1✔
35
    sinon.restore();
1✔
36
  });
37

38
  it('does not resolve client version if specified explicitly', () => {
1✔
39
    const knexInstance = knex({
1✔
40
      client: 'postgres',
41
      version: '10.5',
42
      connection: {
43
        pool: {},
44
      },
45
    });
46
    return knexInstance.raw('select 1 as 1').then((result) => {
1✔
47
      expect(checkVersionStub.notCalled).to.equal(true);
1✔
48
      knexInstance.destroy();
1✔
49
    });
50
  });
51

52
  it('escape statements correctly', async () => {
1✔
53
    const knexInstance = knex({
1✔
54
      client: 'postgresql',
55
      version: '10.5',
56
      connection: {
57
        pool: {},
58
      },
59
    });
60
    const sql = knexInstance('projects')
1✔
61
      .where('id = 1 UNION SELECT 1, version();', 1)
62
      .toSQL();
63
    expect(sql.sql).to.equal(
1✔
64
      'select * from "projects" where "id = 1 UNION SELECT 1, version();" = ?'
65
    );
66

67
    const sql2 = knexInstance('projects')
1✔
68
      .where('id = 1" UNION SELECT 1, version();', 1)
69
      .toSQL();
70
    expect(sql2.sql).to.equal(
1✔
71
      'select * from "projects" where "id = 1"" UNION SELECT 1, version();" = ?'
72
    );
73
  });
74

75
  it('resolve client version if not specified explicitly', () => {
1✔
76
    const knexInstance = knex({
1✔
77
      client: 'postgresql',
78
      connection: {
79
        pool: {},
80
      },
81
    });
82
    return knexInstance.raw('select 1 as 1').then((result) => {
1✔
83
      expect(checkVersionStub.calledOnce).to.equal(true);
1✔
84
      knexInstance.destroy();
1✔
85
    });
86
  });
87

88
  it('Validates searchPath as Array/String', function () {
1✔
89
    const knexInstance = knex({
1✔
90
      client: 'pg',
91
    });
92

93
    expect(function () {
1✔
94
      knexInstance.client.setSchemaSearchPath(null, {});
1✔
95
    }).to.throw(TypeError);
96

97
    expect(function () {
1✔
98
      knexInstance.client.setSchemaSearchPath(null, 4);
1✔
99
    }).to.throw(TypeError);
100

101
    const fakeQueryFn = function (expectedSearchPath) {
1✔
102
      return {
3✔
103
        query: function (sql, callback) {
104
          try {
3✔
105
            expect(sql).to.equal('set search_path to ' + expectedSearchPath);
3✔
106
            callback(null);
3✔
107
          } catch (error) {
108
            callback(error);
×
109
          }
110
        },
111
      };
112
    };
113

114
    return knexInstance.client
1✔
115
      .setSchemaSearchPath(fakeQueryFn('"public,knex"'), 'public,knex')
116
      .then(function () {
117
        return knexInstance.client.setSchemaSearchPath(
1✔
118
          fakeQueryFn('"public","knex"'),
119
          ['public', 'knex']
120
        );
121
      })
122
      .then(function () {
123
        return knexInstance.client.setSchemaSearchPath(
1✔
124
          fakeQueryFn('"public"'),
125
          'public'
126
        );
127
      });
128
  });
129

130
  it('Uses documented query config as param when providing bindings', () => {
1✔
131
    const knexInstance = knex({
1✔
132
      client: 'postgresql',
133
      connection: {},
134
    });
135
    return knexInstance.raw('select 1 as ?', ['foo']).then((result) => {
1✔
136
      sinon.assert.calledOnce(querySpy);
1✔
137
      sinon.assert.calledWithExactly(
1✔
138
        querySpy,
139
        {
140
          text: 'select 1 as $1',
141
          values: ['foo'],
142
        },
143
        sinon.match.func
144
      );
145
      knexInstance.destroy();
1✔
146
    });
147
  });
148

149
  it("throws a helpful error when pg-query-stream isn't installed", async () => {
1✔
150
    const knexInstance = knex({
1✔
151
      client: 'postgresql',
152
      version: '10.5',
153
      connection: {
154
        pool: {},
155
      },
156
    });
157

158
    const Module = require('module');
1✔
159
    const originalLoad = Module._load;
1✔
160
    const loadStub = sinon
1✔
161
      .stub(Module, '_load')
162
      .callsFake((request, parent, isMain) => {
163
        if (request === 'pg-query-stream') {
1!
164
          const err = new Error("Cannot find module 'pg-query-stream'");
1✔
165
          err.code = 'MODULE_NOT_FOUND';
1✔
166
          throw err;
1✔
167
        }
NEW
168
        return originalLoad(request, parent, isMain);
×
169
      });
170

171
    try {
1✔
172
      expect(() =>
1✔
173
        knexInstance.client._stream(
1✔
174
          {
175
            query() {
NEW
176
              throw new Error('connection.query should not be called');
×
177
            },
178
          },
179
          { sql: 'select 1', bindings: [] },
180
          { on: _.noop, emit: _.noop }
181
        )
182
      ).to.throw(
183
        "knex PostgreSQL query streaming requires the 'pg-query-stream' package. Please install it (e.g. `npm i pg-query-stream`)."
184
      );
185
    } finally {
186
      loadStub.restore();
1✔
187
      await knexInstance.destroy();
1✔
188
    }
189
  });
190

191
  it('does not require pg-query-stream in browser mode', async () => {
1✔
192
    const knexInstance = knex({
1✔
193
      client: 'postgresql',
194
      version: '10.5',
195
      connection: {
196
        pool: {},
197
      },
198
    });
199

200
    const Module = require('module');
1✔
201
    const originalLoad = Module._load;
1✔
202
    const loadStub = sinon
1✔
203
      .stub(Module, '_load')
204
      .callsFake((request, parent, isMain) => {
NEW
205
        if (request === 'pg-query-stream') {
×
NEW
206
          throw new Error('pg-query-stream should not be required in browser');
×
207
        }
NEW
208
        return originalLoad(request, parent, isMain);
×
209
      });
210

211
    const originalBrowser = process.browser;
1✔
212
    process.browser = true;
1✔
213

214
    try {
1✔
215
      const connection = {
1✔
216
        query() {
NEW
217
          throw new Error('connection.query should not be called');
×
218
        },
219
      };
220

221
      await knexInstance.client
1✔
222
        ._stream(
223
          connection,
224
          { sql: 'select 1', bindings: [] },
225
          { on: _.noop, emit: _.noop }
226
        )
227
        .then(
228
          () => {
NEW
229
            throw new Error('expected stream to reject in browser mode');
×
230
          },
231
          (err) => {
232
            expect(err).to.be.instanceOf(TypeError);
1✔
233
            expect(err.message).to.not.include('Please install it');
1✔
234
          }
235
        );
236

237
      expect(loadStub.calledWith('pg-query-stream')).to.equal(false);
1✔
238
    } finally {
239
      process.browser = originalBrowser;
1✔
240
      loadStub.restore();
1✔
241
      await knexInstance.destroy();
1✔
242
    }
243
  });
244

245
  it('rethrows non-MODULE_NOT_FOUND errors when loading pg-query-stream', async () => {
1✔
246
    const knexInstance = knex({
1✔
247
      client: 'postgresql',
248
      version: '10.5',
249
      connection: {
250
        pool: {},
251
      },
252
    });
253

254
    const Module = require('module');
1✔
255
    const originalLoad = Module._load;
1✔
256
    const loadStub = sinon
1✔
257
      .stub(Module, '_load')
258
      .callsFake((request, parent, isMain) => {
259
        if (request === 'pg-query-stream') {
1!
260
          const err = new Error('access denied');
1✔
261
          err.code = 'EACCES';
1✔
262
          throw err;
1✔
263
        }
NEW
264
        return originalLoad(request, parent, isMain);
×
265
      });
266

267
    try {
1✔
268
      const connection = {
1✔
269
        query() {
NEW
270
          throw new Error('connection.query should not be called');
×
271
        },
272
      };
273
      let thrown;
274

275
      try {
1✔
276
        knexInstance.client._stream(
1✔
277
          connection,
278
          { sql: 'select 1', bindings: [] },
279
          { on: _.noop, emit: _.noop }
280
        );
281
      } catch (err) {
282
        thrown = err;
1✔
283
      }
284

285
      expect(thrown).to.be.instanceOf(Error);
1✔
286
      expect(thrown.message).to.equal('access denied');
1✔
287
      expect(thrown.code).to.equal('EACCES');
1✔
288
    } finally {
289
      loadStub.restore();
1✔
290
      await knexInstance.destroy();
1✔
291
    }
292
  });
293
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc