Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ npm install kysely tedious tarn @tediousjs/connection-string@0.5.0

# LibSQL
npm install @libsql/kysely-libsql

# ClickHouse
npm install kysely @clickhouse/client @founderpath/kysely-clickhouse
```

## Generating type definitions
Expand All @@ -54,6 +57,9 @@ DATABASE_URL=Server=mssql;Database=database;User Id=user;Password=password

# LibSQL
DATABASE_URL=libsql://token@host:port/database

# ClickHouse
DATABASE_URL=http://username:password@localhost:8123
```

> If your URL contains a password with special characters, those characters may need to be [percent-encoded](https://en.wikipedia.org/wiki/Percent-encoding#Reserved_characters).
Expand Down
12 changes: 11 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3.9'
version: "3.9"

services:
kysely_codegen_adminer:
Expand Down Expand Up @@ -34,3 +34,13 @@ services:
ports:
- 5433:5432
restart: always
kysely_codegen_clickhouse:
container_name: kysely_codegen_clickhouse
environment:
- CLICKHOUSE_USER=default
- CLICKHOUSE_PASSWORD=password
image: clickhouse/clickhouse-server:latest
ports:
- 8123:8123
- 9000:9000
restart: always
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
"devDependencies": {
"@babel/core": "^7.28.3",
"@babel/eslint-parser": "^7.28.0",
"@clickhouse/client": "1.12.1",
"@founderpath/kysely-clickhouse": "1.7.0",
"@libsql/kysely-libsql": "^0.4.1",
"@robinblomberg/eslint-config-prettier": "^0.1.4",
"@robinblomberg/eslint-config-robinblomberg": "0.30.1",
Expand Down Expand Up @@ -100,6 +102,8 @@
"vitest": "^3.2.4"
},
"peerDependencies": {
"@clickhouse/client": ">=0.2.0 <2.0.0",
"@founderpath/kysely-clickhouse": ">=1.0.0 <2.0.0",
"@libsql/kysely-libsql": ">=0.3.0 <0.5.0",
"@tediousjs/connection-string": ">=0.5.0 <0.6.0",
"better-sqlite3": ">=7.6.2 <13.0.0",
Expand All @@ -112,6 +116,12 @@
"tedious": ">=18.0.0 <20.0.0"
},
"peerDependenciesMeta": {
"@clickhouse/client": {
"optional": true
},
"@founderpath/kysely-clickhouse": {
"optional": true
},
"@libsql/kysely-libsql": {
"optional": true
},
Expand Down
30 changes: 30 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/cli/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ describe(Cli.name, () => {
);
assert(
{ dialect: 'sqlite3' },
'Invalid option: expected one of "bun-sqlite"|"kysely-bun-sqlite"|"libsql"|"mssql"|"mysql"|"postgres"|"sqlite"|"worker-bun-sqlite"',
'Invalid option: expected one of "bun-sqlite"|"clickhouse"|"kysely-bun-sqlite"|"libsql"|"mssql"|"mysql"|"postgres"|"sqlite"|"worker-bun-sqlite"',
);
assert(
{ domains: 'true' },
Expand Down
1 change: 1 addition & 0 deletions src/cli/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type DialectName = z.infer<typeof dialectNameSchema>;

export const dialectNameSchema = z.enum([
'bun-sqlite',
'clickhouse',
'kysely-bun-sqlite',
'libsql',
'mssql',
Expand Down
1 change: 1 addition & 0 deletions src/cli/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export const VALID_DIALECTS = [
'bun-sqlite',
'kysely-bun-sqlite',
'worker-bun-sqlite',
'clickhouse',
];
3 changes: 3 additions & 0 deletions src/generator/dialect.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { DialectName } from '../cli/config';
import { IntrospectorDialect } from '../introspector/dialect';
import type { Adapter } from './adapter';
import { ClickhouseDialect } from './dialects/clickhouse/clickhouse-dialect';
import { KyselyBunSqliteDialect } from './dialects/kysely-bun-sqlite/kysely-bun-sqlite-dialect';
import { LibsqlDialect } from './dialects/libsql/libsql-dialect';
import { MssqlDialect } from './dialects/mssql/mssql-dialect';
Expand All @@ -24,6 +25,8 @@ export const getDialect = (
options?: PostgresDialectOptions,
): GeneratorDialect => {
switch (name) {
case 'clickhouse':
return new ClickhouseDialect();
case 'kysely-bun-sqlite':
return new KyselyBunSqliteDialect();
case 'libsql':
Expand Down
80 changes: 80 additions & 0 deletions src/generator/dialects/clickhouse/clickhouse-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Adapter } from '../../adapter';
import { IdentifierNode } from '../../ast/identifier-node';
import {
JSON_ARRAY_DEFINITION,
JSON_OBJECT_DEFINITION,
JSON_PRIMITIVE_DEFINITION,
JSON_VALUE_DEFINITION,
} from '../../transformer/definitions';

/**
* ClickHouse adapter for kysely-codegen.
* Maps ClickHouse data types to TypeScript types.
*/
export class ClickhouseAdapter extends Adapter {
readonly defaultSchemas: string[] = ['default'];

override readonly definitions = {
JsonArray: JSON_ARRAY_DEFINITION,
JsonObject: JSON_OBJECT_DEFINITION,
JsonPrimitive: JSON_PRIMITIVE_DEFINITION,
JsonValue: JSON_VALUE_DEFINITION,
};

// ClickHouse native data types
override readonly scalars = {
// Boolean
bool: new IdentifierNode('number'), // ClickHouse Bool is an alias for UInt8

// Integer types (signed)
int8: new IdentifierNode('number'),
int16: new IdentifierNode('number'),
int32: new IdentifierNode('number'),
int64: new IdentifierNode('number'), // May overflow JS number (consider bigint)
int128: new IdentifierNode('number'), // Will overflow JS number but ClickHouse node-client returns a number
int256: new IdentifierNode('number'), // Will overflow JS number but ClickHouse node-client returns a number

// Integer types (unsigned)
uint8: new IdentifierNode('number'),
uint16: new IdentifierNode('number'),
uint32: new IdentifierNode('number'),
uint64: new IdentifierNode('number'), // May overflow JS number (consider bigint)
uint128: new IdentifierNode('number'), // Will overflow JS number but ClickHouse node-client returns a number
uint256: new IdentifierNode('number'), // Will overflow JS number but ClickHouse node-client returns a number

// Floating point
float32: new IdentifierNode('number'),
float64: new IdentifierNode('number'),

// Decimal types (arbitrary precision)
// ClickHouse node-client returns a number. Cast to a string in your query if needed.
decimal: new IdentifierNode('number'),
decimal32: new IdentifierNode('number'),
decimal64: new IdentifierNode('number'),
decimal128: new IdentifierNode('number'),
decimal256: new IdentifierNode('number'),

// String types
string: new IdentifierNode('string'),
fixedstring: new IdentifierNode('string'),

// Date and time
date: new IdentifierNode('Date'),
date32: new IdentifierNode('Date'),
datetime: new IdentifierNode('Date'),
datetime64: new IdentifierNode('Date'),

// UUID
uuid: new IdentifierNode('string'),

// IP addresses
ipv4: new IdentifierNode('string'),
ipv6: new IdentifierNode('string'),

// JSON (experimental)
json: new IdentifierNode('JsonObject'),

// Special types
nothing: new IdentifierNode('null'), // Represents only NULL
};
}
11 changes: 11 additions & 0 deletions src/generator/dialects/clickhouse/clickhouse-dialect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { GeneratorDialect } from '../../dialect';

import { ClickHouseIntrospectorDialect } from '../../../introspector/dialects/clickhouse/clickhouse-dialect';
import { ClickhouseAdapter } from './clickhouse-adapter';

export class ClickhouseDialect
extends ClickHouseIntrospectorDialect
implements GeneratorDialect
{
readonly adapter = new ClickhouseAdapter();
}
11 changes: 10 additions & 1 deletion src/generator/generator/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { IdentifierNode, TableIdentifierNode } from '../ast/identifier-node';
import { JsonColumnTypeNode } from '../ast/json-column-type-node';
import { RawExpressionNode } from '../ast/raw-expression-node';
import type { GeneratorDialect } from '../dialect';
import { ClickhouseDialect } from '../dialects/clickhouse/clickhouse-dialect';
import { LibsqlDialect } from '../dialects/libsql/libsql-dialect';
import { MysqlDialect } from '../dialects/mysql/mysql-dialect';
import type { PostgresDialectOptions } from '../dialects/postgres/postgres-dialect';
Expand All @@ -38,6 +39,14 @@ type Test = {
const SNAPSHOTS_DIR = join(__dirname, 'snapshots');

const TESTS: Test[] = [
{
connectionString: 'http://default:password@localhost:8123',
dialect: new ClickhouseDialect(),
name: 'clickhouse',
generateOptions: {
camelCase: false,
},
},
{
connectionString: 'libsql://localhost:8080?tls=0',
dialect: new LibsqlDialect(),
Expand Down Expand Up @@ -239,7 +248,7 @@ describe(generate.name, () => {
db,
dialect,
});
await addExtraColumn(db);
await addExtraColumn(db, dialect);

try {
await generate({
Expand Down
54 changes: 54 additions & 0 deletions src/generator/generator/snapshots/clickhouse.snapshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* This file was generated by kysely-codegen.
* Please do not edit it manually.
*/

export type JsonArray = JsonValue[];

export type JsonObject = {
[x: string]: JsonValue | undefined;
};

export type JsonPrimitive = boolean | number | string | null;

export type JsonValue = JsonArray | JsonObject | JsonPrimitive;

export interface TestDbFooBar {
confirmation_enum: "CONFIRMED" | "UNCONFIRMED";
date: Date;
date32: Date;
datetime: Date;
datetime64: Date;
decimal: number;
decimal128: number;
decimal256: number;
decimal32: number;
decimal64: number;
false: number;
fixed_string: string;
float32: number;
float64: number;
id: string;
int128: number;
int16: number;
int256: number;
int32: number;
int64: number;
int8: number;
json: JsonObject;
low_cardinality_string: string;
nullable_string: string | null;
string: string;
string_array: string[];
true: number;
uint128: number;
uint16: number;
uint256: number;
uint32: number;
uint64: number;
uint8: number;
}

export interface DB {
"test_db.foo_bar": TestDbFooBar;
}
25 changes: 25 additions & 0 deletions src/introspector/dialects/clickhouse/clickhouse-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Type definitions for ClickHouse system tables used in introspection.
*/
export type ClickHouseDB = {
'system.tables': {
database: string;
name: string;
engine: string;
metadata_modification_time: string;
};
'system.columns': {
database: string;
table: string;
name: string;
type: string;
default_kind: string;
default_expression: string;
comment: string;
is_in_partition_key: number;
is_in_sorting_key: number;
is_in_primary_key: number;
is_in_sampling_key: number;
position: number;
};
};
Loading