Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
rohit-kadhe committed Mar 18, 2024
1 parent a1796c3 commit 1333eda
Show file tree
Hide file tree
Showing 23 changed files with 7,913 additions and 1 deletion.
28 changes: 28 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": "standard-with-typescript",
"ignorePatterns": [
"dist/"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"unused-imports"
],
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": [
".*"
]
}
],
"@typescript-eslint/restrict-template-expressions": "off"
}
}
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
# clickhouse-schema
# ClickHouse-Schema

Clickhouse-Schema is a TypeScript library that simplifies working with ClickHouse databases by allowing developers to effortlessly define table schemas in TypeScript. This approach not only ensures robust type safety but also significantly reduces development time by automating the generation of TypeScript types that mirror your ClickHouse schemas.

## Key Benefits

- **Effortless TypeScript Integration**: Instantly define your ClickHouse table schemas within TypeScript, leveraging the full power of type safety without the hassle.
- **Time-Saving Type Generation**: Say goodbye to manually creating TypeScript types for your database tables. Clickhouse-Schema does it for you, automatically generating accurate types from your schema definitions.
- **Simplified Database Management**: Easily translate your TypeScript schema definition into a create table ClickHouse SQL query.
- **Comprehensive Data Type Support**: Utilize ClickHouse's extensive data type system, including complex types like JSON, arrays, enums, and more, right from your TypeScript code.

## Getting Started

### Installation

```bash
npm install clickhouse-schema
# or
yarn add clickhouse-schema
80 changes: 80 additions & 0 deletions core/clickhouse_schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export interface SchemaValue { type: ChDataType, default?: unknown } // Note that default is a string because we can't guarantee that it will be a valid value
/**
* ChSchemaOptions is used to define the options for a clickhouse table schema.
*
* @param database is the database to use for the table
* @param table_name is the name of the table in clickhouse
* @param on_cluster is the name of the cluster to use for the table
* @param primary_key is the primary key for the table. if not specified, order_by must be specified
* @param order_by is the order by clause for the table. if not specified, primary_key must be specified
* @param engine is the engine to use for the table, default is MergeTree()
* @param additional_options is an string array of options that are appended to the end of the create table query
*/
export interface ChSchemaOptions {
database?: string
table_name: string
on_cluster?: string
primary_key?: string
order_by?: string
engine?: string
additional_options?: string[]
}

interface IClickhouseSchema {
GetOptions: () => ChSchemaOptions
GetCreateTableQuery: () => string
GetCreateTableQueryAsList: () => string[]
}

/* This class is used to represent a clickhouse table schema */
export class ClickhouseSchema<ChSchema> implements IClickhouseSchema {
readonly schema: ChSchema
private readonly options: ChSchemaOptions

constructor (schema: ChSchema, options: ChSchemaOptions) {
this.schema = schema
this.options = options
}

GetOptions (): ChSchemaOptions {
return this.options
}

GetCreateTableQuery (): string {
if (this.options.primary_key === undefined && this.options.order_by === undefined) {
throw new Error('One of order_by or primary_key must be specified')
}

const columns = Object.entries(this.schema as Record<string, SchemaValue>)
.map(([name, field]) => {
// Check if default is defined and a string, add single quotes; otherwise, just use the value
const defaultValue = field.default !== undefined
? (typeof field.default === 'string' ? `'${field.default}'` : field.default)
: ''
return `${name} ${field.type}${field.default !== undefined ? ` DEFAULT ${defaultValue}` : ''}`
}
)
.join(',\n')

let additionalOptions = ''
if (this.options.additional_options !== undefined) {
additionalOptions = `${this.options.additional_options.join('\n')}`
}
const createTableQuery = [
`CREATE TABLE IF NOT EXISTS ${this.options.database !== undefined ? `${this.options.database}.` : ''}${this.options.table_name}${this.options.on_cluster !== undefined ? ` ON CLUSTER ${this.options.on_cluster}` : ''}`,
`(\n${columns}\n)`,
`ENGINE = ${this.options.engine ?? 'MergeTree()'}`,
this.options.order_by !== undefined ? `ORDER BY ${this.options.order_by}` : '',
this.options.primary_key !== undefined ? `PRIMARY KEY ${this.options.primary_key}` : '',
additionalOptions
].filter(part => part.trim().length > 0).join('\n')

return createTableQuery
}

GetCreateTableQueryAsList (): string[] {
return this.GetCreateTableQuery().split('\n')
}
}
28 changes: 28 additions & 0 deletions core/infer_schema_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { type ClickhouseSchema } from '@clickhouse-schema-core/clickhouse_schema'
import { type ChDataType, type MapChSchemaTypes } from '@clickhouse-schema-data-types/index'
import { type ChArray } from '@clickhouse-schema-data-types/ch_array'
import { type ChJSON } from '@clickhouse-schema-data-types/ch_json'
import { type ExtractInnerType, type ExtractOuterType } from '@clickhouse-schema-utils/type_inference'
import { type ChEnum } from '@clickhouse-schema-data-types/ch_enum'

/** InferSchemaClickhouseSchemaType is a type that takes a ClickhouseSchema and returns the typescript that it represents */
export type InferClickhouseSchemaType<T extends ClickhouseSchema<any>> = { [K in keyof T['schema']]: InferType<T['schema'][K]['type']> }

/** InferTypeFromMap is a type that takes a string and returns the typescript that it represents */
type InferTypeFromMap<T extends string> = ExtractInnerType<T> extends keyof MapChSchemaTypes
? MapChSchemaTypes[ExtractInnerType<T>]
: ExtractOuterType<T> extends keyof MapChSchemaTypes ?
MapChSchemaTypes[ExtractOuterType<T>]
: unknown

/** InferType is a type that takes a ChDataType and returns the typescript that it represents */
type InferType<T extends ChDataType> =
T extends ChArray<infer ArrayType>
? Array<InferType<ArrayType>>
: T extends ChEnum<infer EnumType>
? keyof EnumType
: T extends ChJSON<infer Schema>
? { [K in keyof T['dataType']]: InferType<Schema[K]['type']> }
: T extends ChDataType
? InferTypeFromMap<T['typeStr']>
: unknown
20 changes: 20 additions & 0 deletions data_types/ch_array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChArray<T extends ChDataType | ChArray<ChDataType>> implements ChDataType {
readonly dataType: T
readonly typeStr: string

constructor (t: T) {
if (t instanceof ChArray) {
this.dataType = new ChArray(t.dataType) as T
} else {
this.dataType = t
}
this.typeStr = this.toString()
}

toString (): string {
if (this.dataType instanceof ChArray) return `Array(${this.dataType.toString()})`
return `Array(${this.dataType})`
}
}
9 changes: 9 additions & 0 deletions data_types/ch_boolean.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChBoolean implements ChDataType {
readonly typeStr: 'Boolean' = 'Boolean' as const

toString (): string {
return this.typeStr
}
}
38 changes: 38 additions & 0 deletions data_types/ch_date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChDate implements ChDataType {
readonly typeStr: 'Date' = 'Date' as const
toString (): string {
return this.typeStr
}
}

export class ChDate32 implements ChDataType {
readonly typeStr: 'Date32' = 'Date32' as const

toString (): string {
return this.typeStr
}
}

export class ChDateTime<T extends string> implements ChDataType {
readonly typeStr: `DateTime(${T})`
constructor (readonly timezone: T) {
this.typeStr = `DateTime(${timezone})`
}

toString (): string {
return this.typeStr
}
}

export class ChDateTime64<T extends number, V extends string> implements ChDataType {
readonly typeStr: `DateTime64(${T}, ${V})`
constructor (readonly precision: T, readonly timezone: V) {
this.typeStr = `DateTime64(${precision}, ${timezone})`
}

toString (): string {
return this.typeStr
}
}
15 changes: 15 additions & 0 deletions data_types/ch_enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChEnum<T extends Record<string, number>> implements ChDataType {
readonly typeStr: string
readonly dataType: T

constructor (enumObj: T) {
this.dataType = enumObj
this.typeStr = this.toString()
}

toString (): string {
return `Enum(${Object.keys(this.dataType).map((key) => `'${key}' = ${this.dataType[key]}`).join(',')})`
}
}
16 changes: 16 additions & 0 deletions data_types/ch_float.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChFloat32 implements ChDataType {
readonly typeStr: 'Float32' = 'Float32' as const

toString (): string {
return this.typeStr
}
}

export class ChFloat64 implements ChDataType {
readonly typeStr: 'Float64' = 'Float64' as const
toString (): string {
return this.typeStr
}
}
53 changes: 53 additions & 0 deletions data_types/ch_integer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChUInt8 implements ChDataType {
readonly typeStr: 'UInt8' = 'UInt8' as const
constructor () {
this.typeStr = 'UInt8'
}

toString (): string {
return this.typeStr
}
}

export class ChUInt16 implements ChDataType {
readonly typeStr: 'UInt16' = 'UInt16' as const
constructor () {
this.typeStr = 'UInt16'
}

toString (): string {
return this.typeStr
}
}

export class ChUInt32 implements ChDataType {
readonly typeStr: 'UInt32' = 'UInt32' as const
toString (): string {
return this.typeStr
}
}

export class ChUInt64 implements ChDataType {
readonly typeStr: 'UInt64' = 'UInt64' as const
toString (): string {
return this.typeStr
}
}

export class ChInt8 implements ChDataType {
readonly typeStr: 'Int8' = 'Int8' as const

toString (): string {
return this.typeStr
}
}

export class ChInt16 implements ChDataType {
readonly typeStr: 'Int16' = 'Int16' as const

toString (): string {
return this.typeStr
}
}
13 changes: 13 additions & 0 deletions data_types/ch_json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type SchemaValue } from '@clickhouse-schema-core/clickhouse_schema'
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChJSON<T extends Record<string, SchemaValue>> implements ChDataType {
readonly typeStr: 'JSON'
constructor (readonly dataType: T) {
this.typeStr = 'JSON'
}

toString (): string {
return this.typeStr
}
}
19 changes: 19 additions & 0 deletions data_types/ch_string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChString implements ChDataType {
readonly typeStr: 'String' = 'String' as const
toString (): string {
return this.typeStr
}
}

export class ChFixedString<T extends number> implements ChDataType {
readonly typeStr: `FixedString(${string})`
constructor (readonly length: T) {
this.typeStr = `FixedString(${length})`
}

toString (): string {
return this.typeStr
}
}
8 changes: 8 additions & 0 deletions data_types/ch_uuid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { type ChDataType } from '@clickhouse-schema-data-types/index'

export class ChUUID implements ChDataType {
readonly typeStr: 'UUID' = 'UUID' as const
toString (): string {
return this.typeStr
}
}
Loading

0 comments on commit 1333eda

Please sign in to comment.