bulk-load.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _events = require("events");
  7. var _writableTrackingBuffer = _interopRequireDefault(require("./tracking-buffer/writable-tracking-buffer"));
  8. var _readableStream = require("readable-stream");
  9. var _token = require("./token/token");
  10. var _message = _interopRequireDefault(require("./message"));
  11. var _packet = require("./packet");
  12. var _errors = require("./errors");
  13. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  14. const FLAGS = {
  15. nullable: 1 << 0,
  16. caseSen: 1 << 1,
  17. updateableReadWrite: 1 << 2,
  18. updateableUnknown: 1 << 3,
  19. identity: 1 << 4,
  20. computed: 1 << 5,
  21. // introduced in TDS 7.2
  22. fixedLenCLRType: 1 << 8,
  23. // introduced in TDS 7.2
  24. sparseColumnSet: 1 << 10,
  25. // introduced in TDS 7.3.B
  26. hidden: 1 << 13,
  27. // introduced in TDS 7.2
  28. key: 1 << 14,
  29. // introduced in TDS 7.2
  30. nullableUnknown: 1 << 15 // introduced in TDS 7.2
  31. };
  32. const DONE_STATUS = {
  33. FINAL: 0x00,
  34. MORE: 0x1,
  35. ERROR: 0x2,
  36. INXACT: 0x4,
  37. COUNT: 0x10,
  38. ATTN: 0x20,
  39. SRVERROR: 0x100
  40. };
  41. class BulkLoad extends _events.EventEmitter {
  42. constructor(table, connectionOptions, {
  43. checkConstraints = false,
  44. fireTriggers = false,
  45. keepNulls = false,
  46. lockTable = false
  47. }, callback) {
  48. if (typeof checkConstraints !== 'boolean') {
  49. throw new TypeError('The "options.checkConstraints" property must be of type boolean.');
  50. }
  51. if (typeof fireTriggers !== 'boolean') {
  52. throw new TypeError('The "options.fireTriggers" property must be of type boolean.');
  53. }
  54. if (typeof keepNulls !== 'boolean') {
  55. throw new TypeError('The "options.keepNulls" property must be of type boolean.');
  56. }
  57. if (typeof lockTable !== 'boolean') {
  58. throw new TypeError('The "options.lockTable" property must be of type boolean.');
  59. }
  60. super();
  61. this.error = undefined;
  62. this.canceled = false;
  63. this.executionStarted = false;
  64. this.table = table;
  65. this.options = connectionOptions;
  66. this.callback = callback;
  67. this.columns = [];
  68. this.columnsByName = {};
  69. this.firstRowWritten = false;
  70. this.streamingMode = false;
  71. this.rowToPacketTransform = new RowTransform(this); // eslint-disable-line no-use-before-define
  72. this.bulkOptions = {
  73. checkConstraints,
  74. fireTriggers,
  75. keepNulls,
  76. lockTable
  77. };
  78. }
  79. addColumn(name, type, {
  80. output = false,
  81. length,
  82. precision,
  83. scale,
  84. objName = name,
  85. nullable = true
  86. }) {
  87. if (this.firstRowWritten) {
  88. throw new Error('Columns cannot be added to bulk insert after the first row has been written.');
  89. }
  90. if (this.executionStarted) {
  91. throw new Error('Columns cannot be added to bulk insert after execution has started.');
  92. }
  93. const column = {
  94. type: type,
  95. name: name,
  96. value: null,
  97. output: output,
  98. length: length,
  99. precision: precision,
  100. scale: scale,
  101. objName: objName,
  102. nullable: nullable
  103. };
  104. if ((type.id & 0x30) === 0x20) {
  105. if (column.length == null && type.resolveLength) {
  106. column.length = type.resolveLength(column);
  107. }
  108. }
  109. if (type.resolvePrecision && column.precision == null) {
  110. column.precision = type.resolvePrecision(column);
  111. }
  112. if (type.resolveScale && column.scale == null) {
  113. column.scale = type.resolveScale(column);
  114. }
  115. this.columns.push(column);
  116. this.columnsByName[name] = column;
  117. }
  118. addRow(...input) {
  119. this.firstRowWritten = true;
  120. let row;
  121. if (input.length > 1 || !input[0] || typeof input[0] !== 'object') {
  122. row = input;
  123. } else {
  124. row = input[0];
  125. } // write each column
  126. if (Array.isArray(row)) {
  127. this.rowToPacketTransform.write(row);
  128. } else {
  129. const object = row;
  130. this.rowToPacketTransform.write(this.columns.map(column => {
  131. return object[column.objName];
  132. }));
  133. }
  134. }
  135. getOptionsSql() {
  136. const addOptions = [];
  137. if (this.bulkOptions.checkConstraints) {
  138. addOptions.push('CHECK_CONSTRAINTS');
  139. }
  140. if (this.bulkOptions.fireTriggers) {
  141. addOptions.push('FIRE_TRIGGERS');
  142. }
  143. if (this.bulkOptions.keepNulls) {
  144. addOptions.push('KEEP_NULLS');
  145. }
  146. if (this.bulkOptions.lockTable) {
  147. addOptions.push('TABLOCK');
  148. }
  149. if (addOptions.length > 0) {
  150. return ` WITH (${addOptions.join(',')})`;
  151. } else {
  152. return '';
  153. }
  154. }
  155. getBulkInsertSql() {
  156. let sql = 'insert bulk ' + this.table + '(';
  157. for (let i = 0, len = this.columns.length; i < len; i++) {
  158. const c = this.columns[i];
  159. if (i !== 0) {
  160. sql += ', ';
  161. }
  162. sql += '[' + c.name + '] ' + c.type.declaration(c);
  163. }
  164. sql += ')';
  165. sql += this.getOptionsSql();
  166. return sql;
  167. }
  168. getTableCreationSql() {
  169. let sql = 'CREATE TABLE ' + this.table + '(\n';
  170. for (let i = 0, len = this.columns.length; i < len; i++) {
  171. const c = this.columns[i];
  172. if (i !== 0) {
  173. sql += ',\n';
  174. }
  175. sql += '[' + c.name + '] ' + c.type.declaration(c);
  176. if (c.nullable !== undefined) {
  177. sql += ' ' + (c.nullable ? 'NULL' : 'NOT NULL');
  178. }
  179. }
  180. sql += '\n)';
  181. return sql;
  182. }
  183. getColMetaData() {
  184. const tBuf = new _writableTrackingBuffer.default(100, null, true); // TokenType
  185. tBuf.writeUInt8(_token.TYPE.COLMETADATA); // Count
  186. tBuf.writeUInt16LE(this.columns.length);
  187. for (let j = 0, len = this.columns.length; j < len; j++) {
  188. const c = this.columns[j]; // UserType
  189. if (this.options.tdsVersion < '7_2') {
  190. tBuf.writeUInt16LE(0);
  191. } else {
  192. tBuf.writeUInt32LE(0);
  193. } // Flags
  194. let flags = FLAGS.updateableReadWrite;
  195. if (c.nullable) {
  196. flags |= FLAGS.nullable;
  197. } else if (c.nullable === undefined && this.options.tdsVersion >= '7_2') {
  198. flags |= FLAGS.nullableUnknown;
  199. }
  200. tBuf.writeUInt16LE(flags); // TYPE_INFO
  201. c.type.writeTypeInfo(tBuf, c, this.options); // ColName
  202. tBuf.writeBVarchar(c.name, 'ucs2');
  203. }
  204. return tBuf.data;
  205. }
  206. setTimeout(timeout) {
  207. this.timeout = timeout;
  208. }
  209. createDoneToken() {
  210. // It might be nice to make DoneToken a class if anything needs to create them, but for now, just do it here
  211. const tBuf = new _writableTrackingBuffer.default(this.options.tdsVersion < '7_2' ? 9 : 13);
  212. tBuf.writeUInt8(_token.TYPE.DONE);
  213. const status = DONE_STATUS.FINAL;
  214. tBuf.writeUInt16LE(status);
  215. tBuf.writeUInt16LE(0); // CurCmd (TDS ignores this)
  216. tBuf.writeUInt32LE(0); // row count - doesn't really matter
  217. if (this.options.tdsVersion >= '7_2') {
  218. tBuf.writeUInt32LE(0); // row count is 64 bits in >= TDS 7.2
  219. }
  220. return tBuf.data;
  221. } // This method switches the BulkLoad object into streaming mode and returns
  222. // a stream.Writable for streaming rows to the server.
  223. getRowStream() {
  224. if (this.firstRowWritten) {
  225. throw new Error('BulkLoad cannot be switched to streaming mode after first row has been written using addRow().');
  226. }
  227. if (this.executionStarted) {
  228. throw new Error('BulkLoad cannot be switched to streaming mode after execution has started.');
  229. }
  230. this.streamingMode = true;
  231. return this.rowToPacketTransform;
  232. }
  233. getMessageStream() {
  234. const message = new _message.default({
  235. type: _packet.TYPE.BULK_LOAD
  236. });
  237. this.rowToPacketTransform.pipe(message);
  238. this.rowToPacketTransform.once('finish', () => {
  239. this.removeListener('cancel', onCancel);
  240. });
  241. const onCancel = () => {
  242. this.rowToPacketTransform.emit('error', (0, _errors.RequestError)('Canceled.', 'ECANCEL'));
  243. this.rowToPacketTransform.destroy();
  244. };
  245. this.once('cancel', onCancel);
  246. return message;
  247. }
  248. cancel() {
  249. if (this.canceled) {
  250. return;
  251. }
  252. this.canceled = true;
  253. this.emit('cancel');
  254. }
  255. }
  256. var _default = BulkLoad;
  257. exports.default = _default;
  258. module.exports = BulkLoad; // A transform that converts rows to packets.
  259. class RowTransform extends _readableStream.Transform {
  260. constructor(bulkLoad) {
  261. super({
  262. writableObjectMode: true
  263. });
  264. this.bulkLoad = bulkLoad;
  265. this.mainOptions = bulkLoad.options;
  266. this.columns = bulkLoad.columns;
  267. this.columnMetadataWritten = false;
  268. }
  269. _transform(row, _encoding, callback) {
  270. if (!this.columnMetadataWritten) {
  271. this.push(this.bulkLoad.getColMetaData());
  272. this.columnMetadataWritten = true;
  273. }
  274. const buf = new _writableTrackingBuffer.default(64, 'ucs2', true);
  275. buf.writeUInt8(_token.TYPE.ROW);
  276. for (let i = 0; i < this.columns.length; i++) {
  277. const c = this.columns[i];
  278. c.type.writeParameterData(buf, {
  279. length: c.length,
  280. scale: c.scale,
  281. precision: c.precision,
  282. value: row[i]
  283. }, this.mainOptions, () => {});
  284. }
  285. this.push(buf.data);
  286. process.nextTick(callback);
  287. }
  288. _flush(callback) {
  289. this.push(this.bulkLoad.createDoneToken());
  290. process.nextTick(callback);
  291. }
  292. }