2023-06-05 20:37:00 +08:00
|
|
|
// Copyright (c) 2015, 2023, Oracle and/or its affiliates.
|
2016-05-16 07:53:23 +08:00
|
|
|
|
2019-03-13 08:02:49 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
2023-02-21 09:43:43 +08:00
|
|
|
// This software is dual-licensed to you under the Universal Permissive License
|
|
|
|
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
|
|
|
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
|
|
|
// either license.
|
2019-03-13 08:02:49 +08:00
|
|
|
//
|
2023-02-21 09:43:43 +08:00
|
|
|
// If you elect to accept the software under the Apache License, Version 2.0,
|
|
|
|
// the following applies:
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
2019-03-13 08:02:49 +08:00
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
2023-02-21 09:43:43 +08:00
|
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
2019-03-13 08:02:49 +08:00
|
|
|
//
|
2023-02-21 09:43:43 +08:00
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
2019-03-13 08:02:49 +08:00
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-05-16 07:53:23 +08:00
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2023-06-05 20:37:00 +08:00
|
|
|
const process = require('process');
|
2020-02-16 08:06:45 +08:00
|
|
|
const { Readable } = require('stream');
|
2016-05-16 07:53:23 +08:00
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
class QueryStream extends Readable {
|
2016-05-16 07:53:23 +08:00
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
constructor(rs) {
|
|
|
|
super({ objectMode: true });
|
|
|
|
this._fetching = false;
|
|
|
|
this._numRows = 0;
|
2018-02-06 11:04:01 +08:00
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// calling open via process.nextTick to allow event handlers to be
|
|
|
|
// registered prior to the events being emitted
|
|
|
|
if (rs) {
|
|
|
|
process.nextTick(() => {
|
|
|
|
this._open(rs);
|
2019-11-19 10:27:52 +08:00
|
|
|
});
|
2020-02-16 08:06:45 +08:00
|
|
|
}
|
2016-05-16 07:53:23 +08:00
|
|
|
}
|
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// called by readable.destroy() and ensures that the result set is closed if
|
|
|
|
// it has not already been closed (never called directly)
|
|
|
|
async _destroy(err, cb) {
|
|
|
|
if (this._resultSet) {
|
|
|
|
const rs = this._resultSet;
|
|
|
|
this._resultSet = null;
|
|
|
|
if (this._fetching) {
|
|
|
|
await new Promise(resolve =>
|
|
|
|
this.once('_doneFetching', resolve));
|
|
|
|
}
|
|
|
|
try {
|
2023-02-21 09:33:06 +08:00
|
|
|
await rs._impl.close();
|
2020-02-16 08:06:45 +08:00
|
|
|
} catch (closeErr) {
|
|
|
|
cb(closeErr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cb(err);
|
2018-02-06 10:47:51 +08:00
|
|
|
}
|
2016-05-16 07:53:23 +08:00
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// called when the query stream is to be associated with a result set; this
|
|
|
|
// takes place when the query stream if constructed (if a result set is known
|
|
|
|
// at that point) or by Connection.execute() when the result set is ready
|
|
|
|
_open(rs) {
|
|
|
|
this._resultSet = rs;
|
2018-02-06 10:47:51 +08:00
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// trigger the event listener that may have been added in _read() now that
|
|
|
|
// the result set is ready
|
|
|
|
this.emit('open');
|
2016-05-16 07:53:23 +08:00
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// emit a metadata event as a convenience to users
|
|
|
|
this.emit('metadata', rs.metaData);
|
2016-05-16 07:53:23 +08:00
|
|
|
}
|
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// called by readable.read() and pushes rows to the internal queue maintained
|
|
|
|
// by the stream implementation (never called directly) appropriate
|
|
|
|
async _read() {
|
2016-05-16 07:53:23 +08:00
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// still waiting on the result set to be added via _open() so add an event
|
|
|
|
// listener to retry when ready
|
|
|
|
if (!this._resultSet) {
|
|
|
|
this.once('open', this._read);
|
2018-02-06 10:47:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
// using the JS getRow() to leverage the JS row cache; the result set's
|
|
|
|
// _allowGetRowCall is set to true to allow the call for query streams
|
|
|
|
// created via ResultSet.toQueryStream()
|
|
|
|
try {
|
|
|
|
this._fetching = true;
|
|
|
|
this._resultSet._allowGetRowCall = true;
|
|
|
|
const row = await this._resultSet.getRow();
|
|
|
|
if (row) {
|
|
|
|
this.push(row);
|
|
|
|
} else {
|
|
|
|
this.push(null);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
this.destroy(err);
|
2021-08-01 07:39:42 +08:00
|
|
|
} finally {
|
|
|
|
this._fetching = false;
|
2021-10-11 10:35:20 +08:00
|
|
|
if (this._resultSet) {
|
2021-08-01 07:39:42 +08:00
|
|
|
this._resultSet._allowGetRowCall = false;
|
|
|
|
} else {
|
|
|
|
this.emit('_doneFetching');
|
|
|
|
}
|
2018-02-06 10:47:51 +08:00
|
|
|
}
|
2016-05-16 07:53:23 +08:00
|
|
|
}
|
|
|
|
|
2020-02-16 08:06:45 +08:00
|
|
|
}
|
2016-05-16 07:53:23 +08:00
|
|
|
|
|
|
|
module.exports = QueryStream;
|