Skip to content

Commit

Permalink
feat: add range error validation for datatypes (#1594)
Browse files Browse the repository at this point in the history
* chore: add range error validation for date types

* add validation tests for numbers

* remove only

* add money validation

* chore: remove un-needed types

---------

Co-authored-by: mShan0 <96149598+mShan0@users.noreply.github.com>
  • Loading branch information
MichaelSun90 and mShan0 committed Jan 31, 2024
1 parent 6a961b5 commit 19cb073
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/data-type.ts
Expand Up @@ -80,7 +80,7 @@ export interface DataType {
generateTypeInfo(parameter: ParameterData, options: InternalConnectionOptions): Buffer;
generateParameterLength(parameter: ParameterData, options: InternalConnectionOptions): Buffer;
generateParameterData(parameter: ParameterData, options: InternalConnectionOptions): Generator<Buffer, void>;
validate(value: any, collation: Collation | undefined): any; // TODO: Refactor 'any' and replace with more specific type.
validate(value: any, collation: Collation | undefined, options?: InternalConnectionOptions): any; // TODO: Refactor 'any' and replace with more specific type.

hasTableName?: boolean;

Expand Down
19 changes: 17 additions & 2 deletions src/data-types/date.ts
Expand Up @@ -7,6 +7,9 @@ const EPOCH_DATE = LocalDate.ofYearDay(1, 1);
const NULL_LENGTH = Buffer.from([0x00]);
const DATA_LENGTH = Buffer.from([0x03]);

const MIN_DATE = new globalDate('January 1, 0001');
const MAX_DATE = new globalDate('December 31, 9999');

const Date: DataType = {
id: 0x28,
type: 'DATEN',
Expand Down Expand Up @@ -35,7 +38,7 @@ const Date: DataType = {

const value = parameter.value as any; // Temporary solution. Remove 'any' later.

let date;
let date: LocalDate;
if (options.useUTC) {
date = LocalDate.of(value.getUTCFullYear(), value.getUTCMonth() + 1, value.getUTCDate());
} else {
Expand All @@ -49,7 +52,7 @@ const Date: DataType = {
},

// TODO: value is technically of type 'unknown'.
validate: function(value): null | Date {
validate: function(value, collation, options): null | Date {
if (value == null) {
return null;
}
Expand All @@ -58,6 +61,18 @@ const Date: DataType = {
value = new globalDate(globalDate.parse(value));
}

value = value as Date;

// TODO: check date range: January 1, 0001, through December 31, 9999
// : time range: 00:00:00 through 23:59:59.997
if (options && options.useUTC) {
value = new globalDate(value.toUTCString());
}

if (value < MIN_DATE || value > MAX_DATE) {
throw new TypeError('Out of range.');
}

if (isNaN(value)) {
throw new TypeError('Invalid date.');
}
Expand Down
19 changes: 17 additions & 2 deletions src/data-types/datetime.ts
Expand Up @@ -6,6 +6,9 @@ const EPOCH_DATE = LocalDate.ofYearDay(1900, 1);
const NULL_LENGTH = Buffer.from([0x00]);
const DATA_LENGTH = Buffer.from([0x08]);

const MIN_DATE = new Date('January 1, 1753');
const MAX_DATE = new Date('December 31, 9999');

const DateTime: DataType = {
id: 0x3D,
type: 'DATETIME',
Expand Down Expand Up @@ -34,7 +37,7 @@ const DateTime: DataType = {

const value = parameter.value as any; // Temporary solution. Remove 'any' later.

let date;
let date: LocalDate;
if (options.useUTC) {
date = LocalDate.of(value.getUTCFullYear(), value.getUTCMonth() + 1, value.getUTCDate());
} else {
Expand Down Expand Up @@ -72,7 +75,7 @@ const DateTime: DataType = {
},

// TODO: type 'any' needs to be revisited.
validate: function(value): null | number {
validate: function(value: any, collation, options): null | number {
if (value == null) {
return null;
}
Expand All @@ -81,6 +84,18 @@ const DateTime: DataType = {
value = new Date(Date.parse(value));
}

value = value as Date;

// TODO: check date range: January 1, 1753, through December 31, 9999
// : time range: 00:00:00 through 23:59:59.997
if (options && options.useUTC) {
value = new Date(value.toUTCString());
}

if (value < MIN_DATE || value > MAX_DATE) {
throw new TypeError('Out of range.');
}

if (isNaN(value)) {
throw new TypeError('Invalid date.');
}
Expand Down
19 changes: 17 additions & 2 deletions src/data-types/datetime2.ts
Expand Up @@ -5,6 +5,9 @@ import WritableTrackingBuffer from '../tracking-buffer/writable-tracking-buffer'
const EPOCH_DATE = LocalDate.ofYearDay(1, 1);
const NULL_LENGTH = Buffer.from([0x00]);

const MIN_DATE = new Date('January 1, 0001');
const MAX_DATE = new Date('December 31, 9999');

const DateTime2: DataType & { resolveScale: NonNullable<DataType['resolveScale']> } = {
id: 0x2A,
type: 'DATETIME2N',
Expand Down Expand Up @@ -64,7 +67,7 @@ const DateTime2: DataType & { resolveScale: NonNullable<DataType['resolveScale']
const buffer = new WritableTrackingBuffer(16);
scale = scale!;

let timestamp;
let timestamp: number;
if (options.useUTC) {
timestamp = ((value.getUTCHours() * 60 + value.getUTCMinutes()) * 60 + value.getUTCSeconds()) * 1000 + value.getUTCMilliseconds();
} else {
Expand Down Expand Up @@ -102,7 +105,7 @@ const DateTime2: DataType & { resolveScale: NonNullable<DataType['resolveScale']
yield buffer.data;
},

validate: function(value): null | number {
validate: function(value: any, collation, options): null | number {
if (value == null) {
return null;
}
Expand All @@ -111,6 +114,18 @@ const DateTime2: DataType & { resolveScale: NonNullable<DataType['resolveScale']
value = new Date(Date.parse(value));
}

value = value as Date;

// TODO: check date range: January 1, 0001, through December 31, 9999
// : time range: 00:00:00 through 23:59:59.997
if (options && options.useUTC) {
value = new Date(value.toUTCString());
}

if (value < MIN_DATE || value > MAX_DATE) {
throw new TypeError('Out of range.');
}

if (isNaN(value)) {
throw new TypeError('Invalid date.');
}
Expand Down
19 changes: 17 additions & 2 deletions src/data-types/datetimeoffset.ts
Expand Up @@ -5,6 +5,9 @@ import WritableTrackingBuffer from '../tracking-buffer/writable-tracking-buffer'
const EPOCH_DATE = LocalDate.ofYearDay(1, 1);
const NULL_LENGTH = Buffer.from([0x00]);

const MIN_DATE = new Date('January 1, 0001');
const MAX_DATE = new Date('December 31, 9999');

const DateTimeOffset: DataType & { resolveScale: NonNullable<DataType['resolveScale']> } = {
id: 0x2B,
type: 'DATETIMEOFFSETN',
Expand Down Expand Up @@ -62,7 +65,7 @@ const DateTimeOffset: DataType & { resolveScale: NonNullable<DataType['resolveSc
const buffer = new WritableTrackingBuffer(16);
scale = scale!;

let timestamp;
let timestamp: number;
timestamp = ((value.getUTCHours() * 60 + value.getUTCMinutes()) * 60 + value.getUTCSeconds()) * 1000 + value.getMilliseconds();
timestamp = timestamp * Math.pow(10, scale - 3);
timestamp += (value.nanosecondDelta != null ? value.nanosecondDelta : 0) * Math.pow(10, scale);
Expand Down Expand Up @@ -92,7 +95,7 @@ const DateTimeOffset: DataType & { resolveScale: NonNullable<DataType['resolveSc
buffer.writeInt16LE(offset);
yield buffer.data;
},
validate: function(value): null | number {
validate: function(value: any, collation, options): null | number {
if (value == null) {
return null;
}
Expand All @@ -101,6 +104,18 @@ const DateTimeOffset: DataType & { resolveScale: NonNullable<DataType['resolveSc
value = new Date(Date.parse(value));
}

value = value as Date;

// TODO: check date range: January 1, 0001, through December 31, 9999
// : time range: 00:00:00 through 23:59:59.997
if (options && options.useUTC) {
value = new Date(value.toUTCString());
}

if (value < MIN_DATE || value > MAX_DATE) {
throw new TypeError('Out of range.');
}

if (isNaN(value)) {
throw new TypeError('Invalid date.');
}
Expand Down
8 changes: 8 additions & 0 deletions src/data-types/money.ts
Expand Up @@ -49,6 +49,14 @@ const Money: DataType = {
if (isNaN(value)) {
throw new TypeError('Invalid number.');
}
// money: -922337203685477.5808 to 922337203685477.5807
// in javascript -922337203685477.5808 === -922337203685477.6
// 922337203685477.5807 === 922337203685477.6
// javascript number doesn't have enough precision.
if (value < -922337203685477.6 || value > 922337203685477.6) {
throw new TypeError('Value must be between -922337203685477.5808 and 922337203685477.5807, inclusive.');
}

return value;
}
};
Expand Down
17 changes: 15 additions & 2 deletions src/data-types/smalldatetime.ts
Expand Up @@ -4,6 +4,9 @@ import DateTimeN from './datetimen';
const EPOCH_DATE = new Date(1900, 0, 1);
const UTC_EPOCH_DATE = new Date(Date.UTC(1900, 0, 1));

const MIN_DATE = new Date(1900, 1, 1);
const MAX_DATE = new Date(2079, 5, 6, 23, 59, 59, 0);

const DATA_LENGTH = Buffer.from([0x04]);
const NULL_LENGTH = Buffer.from([0x00]);

Expand Down Expand Up @@ -35,7 +38,7 @@ const SmallDateTime: DataType = {

const buffer = Buffer.alloc(4);

let days, dstDiff, minutes;
let days: number, dstDiff: number, minutes: number;
if (options.useUTC) {
days = Math.floor((parameter.value.getTime() - UTC_EPOCH_DATE.getTime()) / (1000 * 60 * 60 * 24));
minutes = (parameter.value.getUTCHours() * 60) + parameter.value.getUTCMinutes();
Expand All @@ -51,7 +54,7 @@ const SmallDateTime: DataType = {
yield buffer;
},

validate: function(value): null | Date {
validate: function(value, collation, options): null | Date {
if (value == null) {
return null;
}
Expand All @@ -60,6 +63,16 @@ const SmallDateTime: DataType = {
value = new Date(Date.parse(value));
}

value = value as Date;

if (options && options.useUTC) {
value = new Date(value.toUTCString());
}

if (value < MIN_DATE || value > MAX_DATE) {
throw new TypeError('Out of range.');
}

if (isNaN(value)) {
throw new TypeError('Invalid date.');
}
Expand Down

0 comments on commit 19cb073

Please sign in to comment.