-
Notifications
You must be signed in to change notification settings - Fork 11
feat: support decimal #56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
cea4280
c684440
a130121
88de8a7
5c7d3d6
16cab31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -522,6 +522,30 @@ abstract class SenderBufferBase implements SenderBuffer { | |
| } | ||
| } | ||
| } | ||
|
|
||
| /* eslint-disable @typescript-eslint/no-unused-vars */ | ||
| /** | ||
| * Decimal columns are only supported since protocol v3. | ||
| * | ||
| * @throws Error indicating decimals are not supported in v1 and v2 | ||
| */ | ||
| decimalColumnText(_name: string, _value: string | number): SenderBuffer { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ideally signature should match with the one in buffer v3. |
||
| throw new Error("Decimals are not supported in protocol v1/v2"); | ||
| } | ||
|
|
||
| /** | ||
| * Decimal columns are only supported since protocol v3. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. parameter descriptions are missing from js-doc. |
||
| * | ||
| * @throws Error indicating decimals are not supported in v1 and v2 | ||
| */ | ||
| decimalColumnUnscaled( | ||
| _name: string, | ||
| _unscaled: Int8Array | bigint, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ideally signature should match with the one in buffer v3. |
||
| _scale: number, | ||
| ): SenderBuffer { | ||
| throw new Error("Decimals are not supported in protocol v1/v2"); | ||
| } | ||
| /* eslint-enable @typescript-eslint/no-unused-vars */ | ||
| } | ||
|
|
||
| export { SenderBufferBase }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| // @ts-check | ||
| import { SenderOptions } from "../options"; | ||
| import { SenderBuffer } from "./index"; | ||
| import { bigintToTwosComplementBytes } from "../utils"; | ||
| import { SenderBufferV2 } from "./bufferv2"; | ||
| import { validateDecimalText } from "../validation"; | ||
|
|
||
| // Entity type constants for protocol v3. | ||
| const ENTITY_TYPE_DECIMAL: number = 23; | ||
|
|
||
| // ASCII code for equals sign used in binary protocol. | ||
| const EQUALS_SIGN: number = "=".charCodeAt(0); | ||
|
|
||
| /** | ||
| * Buffer implementation for protocol version 3. | ||
| * | ||
| * Provides support for decimals. | ||
| */ | ||
| class SenderBufferV3 extends SenderBufferV2 { | ||
| /** | ||
| * Creates a new SenderBufferV3 instance. | ||
| * | ||
| * @param {SenderOptions} options - Sender configuration object. | ||
| * | ||
| * See SenderOptions documentation for detailed description of configuration options. | ||
| */ | ||
| constructor(options: SenderOptions) { | ||
| super(options); | ||
| } | ||
|
|
||
| /** | ||
| * Writes a decimal value into the buffer using the text format. | ||
| * | ||
| * Use it to insert into DECIMAL database columns. | ||
| * | ||
| * @param {string} name - Column name. | ||
| * @param {number} value - Column value, accepts only number/string values. | ||
| * @returns {Sender} Returns with a reference to this buffer. | ||
| */ | ||
| decimalColumnText( | ||
| name: string, | ||
| value: string | number | null | undefined, | ||
| ): SenderBuffer { | ||
| let str = ""; | ||
| if (typeof value === "string") { | ||
| validateDecimalText(value); | ||
| str = value; | ||
| } else if (typeof value === "number") { | ||
| str = value.toString(); | ||
| } else if (value === null || value === undefined) { | ||
| return this; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is inconsistent with the current API.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, if we accepted null/undefined as column value, I would add a test like the below: |
||
| } else { | ||
| throw new TypeError(`Invalid decimal value type: ${typeof value}`); | ||
| } | ||
| this.writeColumn(name, str, () => { | ||
| this.checkCapacity([str], 1); | ||
| this.write(str); | ||
| this.write("d"); | ||
| }); | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Writes a decimal value into the buffer using the binary format. | ||
| * | ||
| * Use it to insert into DECIMAL database columns. | ||
| * | ||
| * @param {string} name - Column name. | ||
| * @param {number} unscaled - The unscaled value of the decimal in two's | ||
| * complement representation and big-endian byte order. | ||
| * An empty array represents the NULL value. | ||
| * @param {number} scale - The scale of the decimal value. | ||
| * @returns {Sender} Returns with a reference to this buffer. | ||
| */ | ||
| decimalColumnUnscaled( | ||
| name: string, | ||
| unscaled: Int8Array | bigint | null | undefined, | ||
| scale: number, | ||
| ): SenderBuffer { | ||
| if (scale < 0 || scale > 76) { | ||
| throw new RangeError("Scale must be between 0 and 76"); | ||
| } | ||
| let arr: number[]; | ||
| if (typeof unscaled === "bigint") { | ||
| arr = bigintToTwosComplementBytes(unscaled); | ||
| } else if (unscaled instanceof Int8Array) { | ||
| arr = Array.from(unscaled); | ||
| } else if (unscaled === null || unscaled === undefined) { | ||
| return this; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is inconsistent with the current API. |
||
| } else { | ||
| throw new TypeError( | ||
| `Invalid unscaled value type: ${typeof unscaled}, expected Int8Array or bigint`, | ||
| ); | ||
| } | ||
| if (arr.length > 127) { | ||
| throw new RangeError( | ||
| "Unscaled value length must be between 0 and 127 bytes", | ||
| ); | ||
| } | ||
| this.writeColumn(name, unscaled, () => { | ||
| this.checkCapacity([], 4 + arr.length); | ||
| this.writeByte(EQUALS_SIGN); | ||
| this.writeByte(ENTITY_TYPE_DECIMAL); | ||
| this.writeByte(scale); | ||
| this.writeByte(arr.length); | ||
| for (let i = 0; i < arr.length; i++) { | ||
| let byte = arr[i]; | ||
| if (byte > 255 || byte < -128) { | ||
| throw new RangeError( | ||
| `Unscaled value contains invalid byte [index=${i}, value=${byte}]`, | ||
| ); | ||
| } | ||
| if (byte > 127) { | ||
| byte -= 256; | ||
| } | ||
| this.writeByte(byte); | ||
| } | ||
| }); | ||
| return this; | ||
| } | ||
| } | ||
|
|
||
| export { SenderBufferV3 }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,10 +6,12 @@ import { | |
| PROTOCOL_VERSION_V1, | ||
| PROTOCOL_VERSION_V2, | ||
| PROTOCOL_VERSION_AUTO, | ||
| PROTOCOL_VERSION_V3, | ||
| } from "../options"; | ||
| import { TimestampUnit } from "../utils"; | ||
| import { SenderBufferV1 } from "./bufferv1"; | ||
| import { SenderBufferV2 } from "./bufferv2"; | ||
| import { SenderBufferV3 } from "./bufferv3"; | ||
|
|
||
| // Default initial buffer size in bytes (64 KB). | ||
| const DEFAULT_BUFFER_SIZE = 65536; // 64 KB | ||
|
|
@@ -26,6 +28,8 @@ const DEFAULT_MAX_BUFFER_SIZE = 104857600; // 100 MB | |
| */ | ||
| function createBuffer(options: SenderOptions): SenderBuffer { | ||
| switch (options.protocol_version) { | ||
| case PROTOCOL_VERSION_V3: | ||
| return new SenderBufferV3(options); | ||
| case PROTOCOL_VERSION_V2: | ||
| return new SenderBufferV2(options); | ||
| case PROTOCOL_VERSION_V1: | ||
|
|
@@ -170,6 +174,35 @@ interface SenderBuffer { | |
| unit: TimestampUnit, | ||
| ): SenderBuffer; | ||
|
|
||
| /** | ||
| * Writes a decimal value into the buffer using the text format. | ||
| * | ||
| * Use it to insert into DECIMAL database columns. | ||
| * | ||
| * @param {string} name - Column name. | ||
| * @param {number} value - Column value, accepts only number/string values. | ||
| * @returns {Sender} Returns with a reference to this buffer. | ||
| */ | ||
| decimalColumnText(name: string, value: string | number): SenderBuffer; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ideally signature should match with the one in buffer v3. |
||
|
|
||
| /** | ||
| * Writes a decimal value into the buffer using the binary format. | ||
| * | ||
| * Use it to insert into DECIMAL database columns. | ||
| * | ||
| * @param {string} name - Column name. | ||
| * @param {number} unscaled - The unscaled value of the decimal in two's | ||
| * complement representation and big-endian byte order. | ||
| * An empty array represents the NULL value. | ||
| * @param {number} scale - The scale of the decimal value. | ||
| * @returns {Sender} Returns with a reference to this buffer. | ||
| */ | ||
| decimalColumnUnscaled( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ideally signature should match with the one in buffer v3. |
||
| name: string, | ||
| unscaled: Int8Array | bigint, | ||
| scale: number, | ||
| ): SenderBuffer; | ||
|
|
||
| /** | ||
| * Closes the row after writing the designated timestamp into the buffer. | ||
| * | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parameter descriptions are missing from js-doc.
if not added, it will not be included in the documentation.