Skip to content

Commit 6dc290a

Browse files
author
prodrigues
committed
add blob column type to singlestore
1 parent 74a51ae commit 6dc290a

File tree

9 files changed

+268
-41
lines changed

9 files changed

+268
-41
lines changed

drizzle-kit/src/introspect-singlestore.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ const singlestoreImportsList = new Set([
3030
'float',
3131
'int',
3232
'json',
33+
'blob',
3334
// TODO: add new type BSON
34-
// TODO: add new type Blob
3535
// TODO: add new type UUID
3636
// TODO: add new type GUID
3737
// TODO: add new type Vector
@@ -789,6 +789,12 @@ const column = (
789789
return out;
790790
}
791791

792+
if (lowered === 'blob') {
793+
let out = `${casing(name)}: blob(${dbColumnName({ name, casing: rawCasing })})`;
794+
out += defaultValue ? `.default(${mapColumnDefault(defaultValue)})` : '';
795+
return out;
796+
}
797+
792798
console.log('uknown', type);
793799
return `// Warning: Can't parse ${type} from database\n\t// ${type}Type: ${type}("${name}")`;
794800
};

drizzle-kit/src/serializer/singlestoreSerializer.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,6 @@ export const generateSingleStoreSnapshot = (
150150
columnToSet.default = column.default;
151151
}
152152
}
153-
// if (['blob', 'text', 'json'].includes(column.getSQLType())) {
154-
// columnToSet.default = `(${columnToSet.default})`;
155-
// }
156153
}
157154
}
158155
columnsObject[column.name] = columnToSet;

drizzle-kit/src/snapshotsDiffer.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2686,6 +2686,19 @@ export const applyMysqlSnapshotsDiff = async (
26862686
};
26872687
};
26882688

2689+
// This is necessary to make sure that BigInt is serialized to Number
2690+
// at the diffSchemasOrTables function. Ohterwise, it will be serialized to BigInt
2691+
// and the diff will throw the following error
2692+
// "TypeError: Do not know how to serialize a BigInt"
2693+
2694+
declare global {
2695+
interface BigInt {
2696+
toJSON(): Number;
2697+
}
2698+
}
2699+
2700+
BigInt.prototype.toJSON = function () { return Number(this) }
2701+
26892702
export const applySingleStoreSnapshotsDiff = async (
26902703
json1: SingleStoreSchemaSquashed,
26912704
json2: SingleStoreSchemaSquashed,

drizzle-kit/tests/push/singlestore.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SQL, sql } from 'drizzle-orm';
33
import {
44
bigint,
55
binary,
6+
blob,
67
char,
78
date,
89
datetime,
@@ -249,6 +250,14 @@ const singlestoreSuite: DialectSuite = {
249250
columnNotNull: binary('column_not_null', { length: 1 }).notNull(),
250251
columnDefault: binary('column_default', { length: 12 }),
251252
}),
253+
allBlobs: singlestoreTable('all_blobs', {
254+
bigIntSimple: blob('big_int_simple', { mode: 'bigint' }),
255+
bigIntColumnNotNull: blob('big_int_column_not_null', { mode: 'bigint' }).notNull(),
256+
bigIntColumnDefault: blob('big_int_column_default', { mode: 'bigint' }).default(BigInt(12)),
257+
jsonSimple: blob('json_simple', { mode: 'json' }),
258+
jsonColumnNotNull: blob('json_column_not_null', { mode: 'json' }).notNull(),
259+
jsonColumnDefault: blob('json_column_default', { mode: 'json' }).default('{"hello":"world"}'),
260+
})
252261
};
253262

254263
const { statements } = await diffTestSchemasPushSingleStore(

drizzle-orm/src/singlestore-core/columns/all.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { bigint } from './bigint.ts';
22
import { binary } from './binary.ts';
3+
import { blob } from './blob.ts';
34
import { boolean } from './boolean.ts';
45
import { char } from './char.ts';
56
import { customType } from './custom.ts';
@@ -25,6 +26,7 @@ import { year } from './year.ts';
2526

2627
export function getSingleStoreColumnBuilders() {
2728
return {
29+
blob,
2830
bigint,
2931
binary,
3032
boolean,
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import type { ColumnBuilderBaseConfig, ColumnBuilderRuntimeConfig, MakeColumnConfig } from '~/column-builder.ts';
2+
import type { ColumnBaseConfig } from '~/column.ts';
3+
import { entityKind } from '~/entity.ts';
4+
import type { AnySingleStoreTable } from '~/singlestore-core/table.ts';
5+
import { type Equal, getColumnNameAndConfig } from '~/utils.ts';
6+
import { SingleStoreColumn, SingleStoreColumnBuilder } from './common.ts';
7+
8+
type BlobMode = 'buffer' | 'json' | 'bigint';
9+
10+
export type SingleStoreBigIntBuilderInitial<TName extends string> = SingleStoreBigIntBuilder<{
11+
name: TName;
12+
dataType: 'bigint';
13+
columnType: 'SingleStoreBigInt';
14+
data: bigint;
15+
driverParam: Buffer;
16+
enumValues: undefined;
17+
}>;
18+
19+
export class SingleStoreBigIntBuilder<T extends ColumnBuilderBaseConfig<'bigint', 'SingleStoreBigInt'>>
20+
extends SingleStoreColumnBuilder<T>
21+
{
22+
static override readonly [entityKind]: string = 'SingleStoreBigIntBuilder';
23+
24+
constructor(name: T['name']) {
25+
super(name, 'bigint', 'SingleStoreBigInt');
26+
}
27+
28+
/** @internal */
29+
override build<TTableName extends string>(
30+
table: AnySingleStoreTable<{ name: TTableName }>,
31+
): SingleStoreBigInt<MakeColumnConfig<T, TTableName>> {
32+
return new SingleStoreBigInt<MakeColumnConfig<T, TTableName>>(table, this.config as ColumnBuilderRuntimeConfig<any>);
33+
}
34+
}
35+
36+
export class SingleStoreBigInt<T extends ColumnBaseConfig<'bigint', 'SingleStoreBigInt'>> extends SingleStoreColumn<T> {
37+
static override readonly [entityKind]: string = 'SingleStoreBigInt';
38+
39+
getSQLType(): string {
40+
return 'blob';
41+
}
42+
43+
override mapFromDriverValue(value: Buffer | Uint8Array): bigint {
44+
if (Buffer.isBuffer(value)) {
45+
return BigInt(value.toString());
46+
}
47+
48+
return BigInt(String.fromCodePoint(...value));
49+
}
50+
51+
override mapToDriverValue(value: bigint): Buffer {
52+
return Buffer.from(value.toString());
53+
}
54+
}
55+
56+
export type SingleStoreBlobJsonBuilderInitial<TName extends string> = SingleStoreBlobJsonBuilder<{
57+
name: TName;
58+
dataType: 'json';
59+
columnType: 'SingleStoreBlobJson';
60+
data: unknown;
61+
driverParam: Buffer;
62+
enumValues: undefined;
63+
}>;
64+
65+
export class SingleStoreBlobJsonBuilder<T extends ColumnBuilderBaseConfig<'json', 'SingleStoreBlobJson'>>
66+
extends SingleStoreColumnBuilder<T>
67+
{
68+
static override readonly [entityKind]: string = 'SingleStoreBlobJsonBuilder';
69+
70+
constructor(name: T['name']) {
71+
super(name, 'json', 'SingleStoreBlobJson');
72+
}
73+
74+
/** @internal */
75+
override build<TTableName extends string>(
76+
table: AnySingleStoreTable<{ name: TTableName }>,
77+
): SingleStoreBlobJson<MakeColumnConfig<T, TTableName>> {
78+
return new SingleStoreBlobJson<MakeColumnConfig<T, TTableName>>(
79+
table,
80+
this.config as ColumnBuilderRuntimeConfig<any>,
81+
);
82+
}
83+
}
84+
85+
export class SingleStoreBlobJson<T extends ColumnBaseConfig<'json', 'SingleStoreBlobJson'>> extends SingleStoreColumn<T> {
86+
static override readonly [entityKind]: string = 'SingleStoreBlobJson';
87+
88+
getSQLType(): string {
89+
return 'blob';
90+
}
91+
92+
override mapFromDriverValue(value: Buffer | Uint8Array | ArrayBuffer): T['data'] {
93+
if (Buffer.isBuffer(value)) {
94+
return JSON.parse(value.toString());
95+
}
96+
97+
// for sqlite durable objects
98+
// eslint-disable-next-line no-instanceof/no-instanceof
99+
if (value instanceof ArrayBuffer) {
100+
const decoder = new TextDecoder();
101+
return JSON.parse(decoder.decode(value));
102+
}
103+
104+
return JSON.parse(String.fromCodePoint(...value));
105+
}
106+
107+
override mapToDriverValue(value: T['data']): Buffer {
108+
return Buffer.from(JSON.stringify(value));
109+
}
110+
}
111+
112+
export type SingleStoreBlobBufferBuilderInitial<TName extends string> = SingleStoreBlobBufferBuilder<{
113+
name: TName;
114+
dataType: 'buffer';
115+
columnType: 'SingleStoreBlobBuffer';
116+
data: Buffer;
117+
driverParam: Buffer;
118+
enumValues: undefined;
119+
}>;
120+
121+
export class SingleStoreBlobBufferBuilder<T extends ColumnBuilderBaseConfig<'buffer', 'SingleStoreBlobBuffer'>>
122+
extends SingleStoreColumnBuilder<T>
123+
{
124+
static override readonly [entityKind]: string = 'SingleStoreBlobBufferBuilder';
125+
126+
constructor(name: T['name']) {
127+
super(name, 'buffer', 'SingleStoreBlobBuffer');
128+
}
129+
130+
/** @internal */
131+
override build<TTableName extends string>(
132+
table: AnySingleStoreTable<{ name: TTableName }>,
133+
): SingleStoreBlobBuffer<MakeColumnConfig<T, TTableName>> {
134+
return new SingleStoreBlobBuffer<MakeColumnConfig<T, TTableName>>(table, this.config as ColumnBuilderRuntimeConfig<any>);
135+
}
136+
}
137+
138+
export class SingleStoreBlobBuffer<T extends ColumnBaseConfig<'buffer', 'SingleStoreBlobBuffer'>> extends SingleStoreColumn<T> {
139+
static override readonly [entityKind]: string = 'SingleStoreBlobBuffer';
140+
141+
getSQLType(): string {
142+
return 'blob';
143+
}
144+
}
145+
146+
export interface BlobConfig<TMode extends BlobMode = BlobMode> {
147+
mode: TMode;
148+
}
149+
150+
/**
151+
* It's recommended to use `text('...', { mode: 'json' })` instead of `blob` in JSON mode, because it supports JSON functions:
152+
* >All JSON functions currently throw an error if any of their arguments are BLOBs because BLOBs are reserved for a future enhancement in which BLOBs will store the binary encoding for JSON.
153+
*
154+
* https://www.sqlite.org/json1.html
155+
*/
156+
export function blob(): SingleStoreBlobJsonBuilderInitial<''>;
157+
export function blob<TMode extends BlobMode = BlobMode>(
158+
config?: BlobConfig<TMode>,
159+
): Equal<TMode, 'bigint'> extends true ? SingleStoreBigIntBuilderInitial<''>
160+
: Equal<TMode, 'buffer'> extends true ? SingleStoreBlobBufferBuilderInitial<''>
161+
: SingleStoreBlobJsonBuilderInitial<''>;
162+
export function blob<TName extends string, TMode extends BlobMode = BlobMode>(
163+
name: TName,
164+
config?: BlobConfig<TMode>,
165+
): Equal<TMode, 'bigint'> extends true ? SingleStoreBigIntBuilderInitial<TName>
166+
: Equal<TMode, 'buffer'> extends true ? SingleStoreBlobBufferBuilderInitial<TName>
167+
: SingleStoreBlobJsonBuilderInitial<TName>;
168+
export function blob(a?: string | BlobConfig, b?: BlobConfig) {
169+
const { name, config } = getColumnNameAndConfig<BlobConfig | undefined>(a, b);
170+
if (config?.mode === 'json') {
171+
return new SingleStoreBlobJsonBuilder(name);
172+
}
173+
if (config?.mode === 'bigint') {
174+
return new SingleStoreBigIntBuilder(name);
175+
}
176+
return new SingleStoreBlobBufferBuilder(name);
177+
}

drizzle-orm/src/singlestore-core/columns/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './blob.ts';
12
export * from './bigint.ts';
23
export * from './binary.ts';
34
export * from './boolean.ts';

drizzle-orm/type-tests/singlestore/tables.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type Equal, Expect } from 'type-tests/utils.ts';
22
import type { BuildColumn } from '~/column-builder.ts';
33
import { eq } from '~/expressions.ts';
44
import {
5+
blob,
56
bigint,
67
binary,
78
boolean,
@@ -836,6 +837,9 @@ Expect<
836837

837838
{
838839
singlestoreTable('all_columns', {
840+
blob: blob('blob'),
841+
blob2: blob('blob2', { mode: 'bigint' }),
842+
blobdef: blob('blobdef').default(0),
839843
bigint: bigint('bigint', { mode: 'number' }),
840844
bigint2: bigint('bigint', { mode: 'number', unsigned: true }),
841845
bigintdef: bigint('bigintdef', { mode: 'number' }).default(0),
@@ -934,6 +938,9 @@ Expect<
934938

935939
{
936940
singlestoreTable('all_columns_without_name', {
941+
blob: blob(),
942+
blob2: blob({ mode: 'bigint' }),
943+
blobdef: blob().default(0),
937944
bigint: bigint({ mode: 'number' }),
938945
bigint2: bigint({ mode: 'number', unsigned: true }),
939946
bigintdef: bigint({ mode: 'number' }).default(0),

0 commit comments

Comments
 (0)