@@ -26,6 +26,7 @@ class Sender {
2626 /** @private */ bufferSize ;
2727 /** @private */ buffer ;
2828 /** @private */ position ;
29+ /** @private */ endOfLastRow ;
2930 /** @private */ hasTable ;
3031 /** @private */ hasSymbols ;
3132 /** @private */ hasColumns ;
@@ -39,18 +40,23 @@ class Sender {
3940 constructor ( bufferSize , jwk = undefined ) {
4041 this . jwk = jwk ;
4142 this . resize ( bufferSize ) ;
43+ this . reset ( ) ;
4244 }
4345
4446 /**
45- * Reinitializes the buffer of the sender. <br>
47+ * Extends the size of the sender's buffer . <br>
4648 * Can be used to increase the size of buffer if overflown.
49+ * The buffer's content is copied into the new buffer.
4750 *
4851 * @param {number } bufferSize - New size of the buffer used by the sender, provided in bytes.
4952 */
5053 resize ( bufferSize ) {
5154 this . bufferSize = bufferSize ;
52- this . buffer = Buffer . alloc ( this . bufferSize + 1 , 0 , 'utf8' ) ;
53- this . reset ( ) ;
55+ const newBuffer = Buffer . alloc ( this . bufferSize + 1 , 0 , 'utf8' ) ;
56+ if ( this . buffer ) {
57+ this . buffer . copy ( newBuffer ) ;
58+ }
59+ this . buffer = newBuffer ;
5460 }
5561
5662 /**
@@ -136,30 +142,32 @@ class Sender {
136142 }
137143
138144 /**
139- * Sends the buffer's content to the database and clears the buffer.
145+ * Sends the buffer's content to the database and compacts the buffer.
146+ * If the last row is not finished it stays in the sender's buffer.
147+ *
148+ * @return {boolean } Returns true if there was data in the buffer to send.
140149 */
141150 async flush ( ) {
142- const data = this . toBuffer ( ) ;
151+ const data = this . toBuffer ( this . endOfLastRow ) ;
152+ if ( ! data ) {
153+ return false ;
154+ }
143155 return new Promise ( ( resolve , reject ) => {
144156 this . socket . write ( data , err => {
145- this . reset ( ) ;
146- err ? reject ( err ) : resolve ( ) ;
157+ compact ( this ) ;
158+ err ? reject ( err ) : resolve ( true ) ;
147159 } ) ;
148160 } ) ;
149161 }
150162
151163 /**
152164 * @ignore
153- * @return {Buffer } Returns a cropped buffer ready to send to the server.
165+ * @return {Buffer } Returns a cropped buffer ready to send to the server or null if there is nothing to send .
154166 */
155- toBuffer ( ) {
156- if ( this . hasTable ) {
157- throw new Error ( "The buffer's content is invalid, row needs to be closed by calling at() or atNow()" ) ;
158- }
159- if ( this . position < 1 ) {
160- throw new Error ( "The buffer is empty" ) ;
161- }
162- return this . buffer . subarray ( 0 , this . position ) ;
167+ toBuffer ( pos = this . position ) {
168+ return pos > 0
169+ ? this . buffer . subarray ( 0 , pos )
170+ : null ;
163171 }
164172
165173 /**
@@ -176,6 +184,7 @@ class Sender {
176184 throw new Error ( "Table name has already been set" ) ;
177185 }
178186 validateTableName ( table ) ;
187+ checkCapacity ( this , [ table ] ) ;
179188 writeEscaped ( this , table ) ;
180189 this . hasTable = true ;
181190 return this ;
@@ -195,11 +204,13 @@ class Sender {
195204 if ( ! this . hasTable || this . hasColumns ) {
196205 throw new Error ( "Symbol can be added only after table name is set and before any column added" ) ;
197206 }
207+ const valueStr = value . toString ( ) ;
208+ checkCapacity ( this , [ name , valueStr ] , 2 + name . length + valueStr . length ) ;
198209 write ( this , ',' ) ;
199210 validateColumnName ( name ) ;
200211 writeEscaped ( this , name ) ;
201212 write ( this , '=' ) ;
202- writeEscaped ( this , value . toString ( ) ) ;
213+ writeEscaped ( this , valueStr ) ;
203214 this . hasSymbols = true ;
204215 return this ;
205216 }
@@ -213,6 +224,7 @@ class Sender {
213224 */
214225 stringColumn ( name , value ) {
215226 writeColumn ( this , name , value , ( ) => {
227+ checkCapacity ( this , [ value ] , 2 + value . length ) ;
216228 write ( this , '"' ) ;
217229 writeEscaped ( this , value , true ) ;
218230 write ( this , '"' ) ;
@@ -229,6 +241,7 @@ class Sender {
229241 */
230242 booleanColumn ( name , value ) {
231243 writeColumn ( this , name , value , ( ) => {
244+ checkCapacity ( this , [ ] , 1 ) ;
232245 write ( this , value ? 't' : 'f' ) ;
233246 } , "boolean" ) ;
234247 return this ;
@@ -243,7 +256,9 @@ class Sender {
243256 */
244257 floatColumn ( name , value ) {
245258 writeColumn ( this , name , value , ( ) => {
246- write ( this , value . toString ( ) ) ;
259+ const valueStr = value . toString ( ) ;
260+ checkCapacity ( this , [ valueStr ] , valueStr . length ) ;
261+ write ( this , valueStr ) ;
247262 } , "number" ) ;
248263 return this ;
249264 }
@@ -260,7 +275,9 @@ class Sender {
260275 throw new Error ( `Value must be an integer, received ${ value } ` ) ;
261276 }
262277 writeColumn ( this , name , value , ( ) => {
263- write ( this , value . toString ( ) ) ;
278+ const valueStr = value . toString ( ) ;
279+ checkCapacity ( this , [ valueStr ] , 1 + valueStr . length ) ;
280+ write ( this , valueStr ) ;
264281 write ( this , 'i' ) ;
265282 } , "number" ) ;
266283 return this ;
@@ -278,7 +295,9 @@ class Sender {
278295 throw new Error ( `Value must be an integer, received ${ value } ` ) ;
279296 }
280297 writeColumn ( this , name , value , ( ) => {
281- write ( this , value . toString ( ) ) ;
298+ const valueStr = value . toString ( ) ;
299+ checkCapacity ( this , [ valueStr ] , 1 + valueStr . length ) ;
300+ write ( this , valueStr ) ;
282301 write ( this , 't' ) ;
283302 } , "number" ) ;
284303 return this ;
@@ -297,8 +316,10 @@ class Sender {
297316 throw new Error ( `The designated timestamp must be of type string, received ${ typeof timestamp } ` ) ;
298317 }
299318 validateDesignatedTimestamp ( timestamp ) ;
319+ const timestampStr = timestamp . toString ( ) ;
320+ checkCapacity ( this , [ ] , 2 + timestampStr . length ) ;
300321 write ( this , ' ' ) ;
301- write ( this , timestamp . toString ( ) ) ;
322+ write ( this , timestampStr ) ;
302323 write ( this , '\n' ) ;
303324 startNewRow ( this ) ;
304325 }
@@ -311,6 +332,7 @@ class Sender {
311332 if ( ! this . hasSymbols && ! this . hasColumns ) {
312333 throw new Error ( "The row must have a symbol or column set before it is closed" ) ;
313334 }
335+ checkCapacity ( this , [ ] , 1 ) ;
314336 write ( this , '\n' ) ;
315337 startNewRow ( this ) ;
316338 }
@@ -338,11 +360,34 @@ async function authenticate(sender, challenge) {
338360}
339361
340362function startNewRow ( sender ) {
363+ sender . endOfLastRow = sender . position ;
341364 sender . hasTable = false ;
342365 sender . hasSymbols = false ;
343366 sender . hasColumns = false ;
344367}
345368
369+ function checkCapacity ( sender , data , base = 0 ) {
370+ let length = base ;
371+ for ( const str of data ) {
372+ length += Buffer . byteLength ( str , 'utf8' ) ;
373+ }
374+ if ( sender . position + length > sender . bufferSize ) {
375+ let newSize = sender . bufferSize ;
376+ do {
377+ newSize += sender . bufferSize ;
378+ } while ( sender . position + length > newSize ) ;
379+ sender . resize ( newSize ) ;
380+ }
381+ }
382+
383+ function compact ( sender ) {
384+ if ( sender . endOfLastRow > 0 ) {
385+ sender . buffer . copy ( sender . buffer , 0 , sender . endOfLastRow , sender . position ) ;
386+ sender . position = sender . position - sender . endOfLastRow ;
387+ sender . endOfLastRow = 0 ;
388+ }
389+ }
390+
346391function writeColumn ( sender , name , value , writeValue , valueType ) {
347392 if ( typeof name !== "string" ) {
348393 throw new Error ( `Column name must be a string, received ${ typeof name } ` ) ;
@@ -353,6 +398,7 @@ function writeColumn(sender, name, value, writeValue, valueType) {
353398 if ( ! sender . hasTable ) {
354399 throw new Error ( "Column can be set only after table name is set" ) ;
355400 }
401+ checkCapacity ( sender , [ name ] , 2 + name . length ) ;
356402 write ( sender , sender . hasColumns ? ',' : ' ' ) ;
357403 validateColumnName ( name ) ;
358404 writeEscaped ( sender , name ) ;
0 commit comments