Skip to content

Commit 867d080

Browse files
committed
MySQL decimal modes
1 parent 00cbce5 commit 867d080

File tree

5 files changed

+164
-9
lines changed

5 files changed

+164
-9
lines changed

drizzle-orm/src/mysql-core/columns/decimal.ts

+141-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnCon
22
import type { ColumnBaseConfig } from '~/column.ts';
33
import { entityKind } from '~/entity.ts';
44
import type { AnyMySqlTable } from '~/mysql-core/table.ts';
5-
import { getColumnNameAndConfig } from '~/utils.ts';
5+
import { type Equal, getColumnNameAndConfig } from '~/utils.ts';
66
import { MySqlColumnBuilderWithAutoIncrement, MySqlColumnWithAutoIncrement } from './common.ts';
77

88
export type MySqlDecimalBuilderInitial<TName extends string> = MySqlDecimalBuilder<{
@@ -67,21 +67,153 @@ export class MySqlDecimal<T extends ColumnBaseConfig<'string', 'MySqlDecimal'>>
6767
}
6868
}
6969

70-
export interface MySqlDecimalConfig {
70+
export type MySqlDecimalNumberBuilderInitial<TName extends string> = MySqlDecimalNumberBuilder<{
71+
name: TName;
72+
dataType: 'number';
73+
columnType: 'MySqlDecimalNumber';
74+
data: number;
75+
driverParam: string;
76+
enumValues: undefined;
77+
}>;
78+
79+
export class MySqlDecimalNumberBuilder<
80+
T extends ColumnBuilderBaseConfig<'number', 'MySqlDecimalNumber'>,
81+
> extends MySqlColumnBuilderWithAutoIncrement<T, MySqlDecimalConfig> {
82+
static override readonly [entityKind]: string = 'MySqlDecimalNumberBuilder';
83+
84+
constructor(name: T['name'], config: MySqlDecimalConfig | undefined) {
85+
super(name, 'number', 'MySqlDecimalNumber');
86+
this.config.precision = config?.precision;
87+
this.config.scale = config?.scale;
88+
this.config.unsigned = config?.unsigned;
89+
}
90+
91+
/** @internal */
92+
override build<TTableName extends string>(
93+
table: AnyMySqlTable<{ name: TTableName }>,
94+
): MySqlDecimalNumber<MakeColumnConfig<T, TTableName>> {
95+
return new MySqlDecimalNumber<MakeColumnConfig<T, TTableName>>(
96+
table,
97+
this.config as ColumnBuilderRuntimeConfig<any, any>,
98+
);
99+
}
100+
}
101+
102+
export class MySqlDecimalNumber<T extends ColumnBaseConfig<'number', 'MySqlDecimalNumber'>>
103+
extends MySqlColumnWithAutoIncrement<T, MySqlDecimalConfig>
104+
{
105+
static override readonly [entityKind]: string = 'MySqlDecimalNumber';
106+
107+
readonly precision: number | undefined = this.config.precision;
108+
readonly scale: number | undefined = this.config.scale;
109+
readonly unsigned: boolean | undefined = this.config.unsigned;
110+
111+
override mapFromDriverValue(value: unknown): number {
112+
if (typeof value === 'number') return value;
113+
114+
return Number(value);
115+
}
116+
117+
override mapToDriverValue = String;
118+
119+
getSQLType(): string {
120+
let type = '';
121+
if (this.precision !== undefined && this.scale !== undefined) {
122+
type += `decimal(${this.precision},${this.scale})`;
123+
} else if (this.precision === undefined) {
124+
type += 'decimal';
125+
} else {
126+
type += `decimal(${this.precision})`;
127+
}
128+
type = type === 'decimal(10,0)' || type === 'decimal(10)' ? 'decimal' : type;
129+
return this.unsigned ? `${type} unsigned` : type;
130+
}
131+
}
132+
133+
export type MySqlDecimalBigIntBuilderInitial<TName extends string> = MySqlDecimalBigIntBuilder<{
134+
name: TName;
135+
dataType: 'bigint';
136+
columnType: 'MySqlDecimalBigInt';
137+
data: bigint;
138+
driverParam: string;
139+
enumValues: undefined;
140+
}>;
141+
142+
export class MySqlDecimalBigIntBuilder<
143+
T extends ColumnBuilderBaseConfig<'bigint', 'MySqlDecimalBigInt'>,
144+
> extends MySqlColumnBuilderWithAutoIncrement<T, MySqlDecimalConfig> {
145+
static override readonly [entityKind]: string = 'MySqlDecimalBigIntBuilder';
146+
147+
constructor(name: T['name'], config: MySqlDecimalConfig | undefined) {
148+
super(name, 'bigint', 'MySqlDecimalBigInt');
149+
this.config.precision = config?.precision;
150+
this.config.scale = config?.scale;
151+
this.config.unsigned = config?.unsigned;
152+
}
153+
154+
/** @internal */
155+
override build<TTableName extends string>(
156+
table: AnyMySqlTable<{ name: TTableName }>,
157+
): MySqlDecimalBigInt<MakeColumnConfig<T, TTableName>> {
158+
return new MySqlDecimalBigInt<MakeColumnConfig<T, TTableName>>(
159+
table,
160+
this.config as ColumnBuilderRuntimeConfig<any, any>,
161+
);
162+
}
163+
}
164+
165+
export class MySqlDecimalBigInt<T extends ColumnBaseConfig<'bigint', 'MySqlDecimalBigInt'>>
166+
extends MySqlColumnWithAutoIncrement<T, MySqlDecimalConfig>
167+
{
168+
static override readonly [entityKind]: string = 'MySqlDecimalBigInt';
169+
170+
readonly precision: number | undefined = this.config.precision;
171+
readonly scale: number | undefined = this.config.scale;
172+
readonly unsigned: boolean | undefined = this.config.unsigned;
173+
174+
override mapFromDriverValue = BigInt;
175+
176+
override mapToDriverValue = String;
177+
178+
getSQLType(): string {
179+
let type = '';
180+
if (this.precision !== undefined && this.scale !== undefined) {
181+
type += `decimal(${this.precision},${this.scale})`;
182+
} else if (this.precision === undefined) {
183+
type += 'decimal';
184+
} else {
185+
type += `decimal(${this.precision})`;
186+
}
187+
type = type === 'decimal(10,0)' || type === 'decimal(10)' ? 'decimal' : type;
188+
return this.unsigned ? `${type} unsigned` : type;
189+
}
190+
}
191+
192+
export interface MySqlDecimalConfig<T extends 'string' | 'number' | 'bigint' = 'string' | 'number' | 'bigint'> {
71193
precision?: number;
72194
scale?: number;
73195
unsigned?: boolean;
196+
mode?: T;
74197
}
75198

76199
export function decimal(): MySqlDecimalBuilderInitial<''>;
77-
export function decimal(
78-
config: MySqlDecimalConfig,
79-
): MySqlDecimalBuilderInitial<''>;
80-
export function decimal<TName extends string>(
200+
export function decimal<TMode extends 'string' | 'number' | 'bigint'>(
201+
config: MySqlDecimalConfig<TMode>,
202+
): Equal<TMode, 'number'> extends true ? MySqlDecimalNumberBuilderInitial<''>
203+
: Equal<TMode, 'bigint'> extends true ? MySqlDecimalBigIntBuilderInitial<''>
204+
: MySqlDecimalBuilderInitial<''>;
205+
export function decimal<TName extends string, TMode extends 'string' | 'number' | 'bigint'>(
81206
name: TName,
82-
config?: MySqlDecimalConfig,
83-
): MySqlDecimalBuilderInitial<TName>;
207+
config?: MySqlDecimalConfig<TMode>,
208+
): Equal<TMode, 'number'> extends true ? MySqlDecimalNumberBuilderInitial<TName>
209+
: Equal<TMode, 'bigint'> extends true ? MySqlDecimalBigIntBuilderInitial<TName>
210+
: MySqlDecimalBuilderInitial<TName>;
84211
export function decimal(a?: string | MySqlDecimalConfig, b: MySqlDecimalConfig = {}) {
85212
const { name, config } = getColumnNameAndConfig<MySqlDecimalConfig>(a, b);
86-
return new MySqlDecimalBuilder(name, config);
213+
const mode = config?.mode;
214+
return mode === 'number'
215+
? new MySqlDecimalNumberBuilder(name, config)
216+
: mode === 'bigint'
217+
? new MySqlDecimalBigIntBuilder(name, config)
218+
: new MySqlDecimalBuilder(name, config);
87219
}

drizzle-orm/src/mysql-core/dialect.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,9 @@ export class MySqlDialect {
11731173
case 'MySqlDateTimeString':
11741174
case 'MySqlTimestampString':
11751175
case 'MySqlFloat':
1176+
case 'MySqlDecimal':
1177+
case 'MySqlDecimalNumber':
1178+
case 'MySqlDecimalBigInt':
11761179
case 'MySqlBigInt64': {
11771180
return sql`cast(${name} as char) as ${sql.identifier(key)}`;
11781181
}

integration-tests/tests/relational/mysql.planetscale.test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -10101,6 +10101,8 @@ test('alltypes', async () => {
1010110101
\`datetime\` datetime,
1010210102
\`datetime_str\` datetime,
1010310103
\`decimal\` decimal,
10104+
\`decimal_num\` decimal(30),
10105+
\`decimal_big\` decimal(30),
1010410106
\`double\` double,
1010510107
\`float\` float,
1010610108
\`int\` int,
@@ -10137,6 +10139,8 @@ test('alltypes', async () => {
1013710139
datetime: new Date(1741743161623),
1013810140
datetimeStr: new Date(1741743161623).toISOString().slice(0, 19).replace('T', ' '),
1013910141
decimal: '47521',
10142+
decimalNum: 9007199254740991,
10143+
decimalBig: 5044565289845416380n,
1014010144
double: 15.35325689124218,
1014110145
enum: 'enV1',
1014210146
float: 1.048596,
@@ -10185,6 +10189,8 @@ test('alltypes', async () => {
1018510189
datetime: new Date('2025-03-12T01:32:42.000Z'),
1018610190
datetimeStr: '2025-03-12 01:32:41',
1018710191
decimal: '47521',
10192+
decimalNum: 9007199254740991,
10193+
decimalBig: 5044565289845416380n,
1018810194
double: 15.35325689124218,
1018910195
float: 1.0486,
1019010196
int: 621,

integration-tests/tests/relational/mysql.schema.ts

+8
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,14 @@ export const allTypesTable = mysqlTable('all_types', {
248248
mode: 'string',
249249
}),
250250
decimal: decimal(),
251+
decimalNum: decimal({
252+
scale: 30,
253+
mode: 'number',
254+
}),
255+
decimalBig: decimal({
256+
scale: 30,
257+
mode: 'bigint',
258+
}),
251259
double: double(),
252260
float: float(),
253261
int: int(),

integration-tests/tests/relational/mysql.test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -12760,6 +12760,8 @@ test('alltypes', async () => {
1276012760
\`datetime\` datetime,
1276112761
\`datetime_str\` datetime,
1276212762
\`decimal\` decimal,
12763+
\`decimal_num\` decimal(30),
12764+
\`decimal_big\` decimal(30),
1276312765
\`double\` double,
1276412766
\`float\` float,
1276512767
\`int\` int,
@@ -12796,6 +12798,8 @@ test('alltypes', async () => {
1279612798
datetime: new Date(1741743161623),
1279712799
datetimeStr: new Date(1741743161623).toISOString().slice(0, 19).replace('T', ' '),
1279812800
decimal: '47521',
12801+
decimalNum: 9007199254740991,
12802+
decimalBig: 5044565289845416380n,
1279912803
double: 15.35325689124218,
1280012804
enum: 'enV1',
1280112805
float: 1.048596,
@@ -12844,6 +12848,8 @@ test('alltypes', async () => {
1284412848
datetime: new Date('2025-03-12T01:32:42.000Z'),
1284512849
datetimeStr: '2025-03-12 01:32:41',
1284612850
decimal: '47521',
12851+
decimalNum: 9007199254740991,
12852+
decimalBig: 5044565289845416380n,
1284712853
double: 15.35325689124218,
1284812854
float: 1.0486,
1284912855
int: 621,

0 commit comments

Comments
 (0)