Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose parseColumnType function #316

Merged
merged 6 commits into from
Sep 23, 2024
Merged
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
22 changes: 17 additions & 5 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ on:
schedule:
- cron: '43 12 * * 6'
push:
branches: [ "main" ]
branches:
- main
paths-ignore:
- '**/*.md'
- 'LICENSE'
- 'benchmarks/**'
- 'examples/**'
pull_request:
paths-ignore:
- '**/*.md'
- 'LICENSE'
- 'benchmarks/**'
- 'examples/**'
workflow_dispatch:

# Declare default permissions as read only.
Expand All @@ -32,12 +44,12 @@ jobs:
# actions: read

steps:
- name: "Checkout code"
- name: 'Checkout code'
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false

- name: "Run analysis"
- name: 'Run analysis'
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
Expand All @@ -59,7 +71,7 @@ jobs:

# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
- name: 'Upload artifact'
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
with:
name: SARIF file
Expand All @@ -68,7 +80,7 @@ jobs:

# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
# 1.7.0 (Common, Node.js, Web)

- (Experimental) Exposed the `parseColumnType` function that takes a string representation of a ClickHouse type (e.g., `FixedString(16)`, `Nullable(Int32)`, etc.) and returns an AST-like object that represents the type. For example:

```ts
for (const type of [
'Int32',
'Array(Nullable(String))',
`Map(Int32, DateTime64(9, 'UTC'))`,
]) {
console.log(`##### Source ClickHouse type: ${type}`)
console.log(parseColumnType(type))
}
```

The above code will output:

```
##### Source ClickHouse type: Int32
{ type: 'Simple', columnType: 'Int32', sourceType: 'Int32' }
##### Source ClickHouse type: Array(Nullable(String))
{
type: 'Array',
value: {
type: 'Nullable',
sourceType: 'Nullable(String)',
value: { type: 'Simple', columnType: 'String', sourceType: 'String' }
},
dimensions: 1,
sourceType: 'Array(Nullable(String))'
}
##### Source ClickHouse type: Map(Int32, DateTime64(9, 'UTC'))
{
type: 'Map',
key: { type: 'Simple', columnType: 'Int32', sourceType: 'Int32' },
value: {
type: 'DateTime64',
timezone: 'UTC',
precision: 9,
sourceType: "DateTime64(9, 'UTC')"
},
sourceType: "Map(Int32, DateTime64(9, 'UTC'))"
}
```

While the original intention was to use this function internally for `Native`/`RowBinaryWithNamesAndTypes` data formats headers parsing, it can be useful for other purposes as well (e.g., interfaces generation, or custom JSON serializers).

NB: currently unsupported source types to parse:

- Geo
- (Simple)AggregateFunction
- Nested
- Old/new experimental JSON
- Dynamic
- Variant

# 1.6.0 (Common, Node.js, Web)

## New features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ describe('abort request', () => {
beforeEach(() => {
client = createTestClient()
})

afterEach(async () => {
await client.close()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ describe('ClickHouse server errors parsing', () => {
// Possible error messages here:
// (since 24.3+, Cloud SMT): Unknown expression identifier 'number' in scope SELECT number AS FR
// (since 23.8+, Cloud RMT): Missing columns: 'number' while processing query: 'SELECT number AS FR', required columns: 'number'
// (since 24.9+): Unknown expression identifier `number` in scope SELECT number AS FR
const errorMessagePattern =
`((?:Missing columns: 'number' while processing query: 'SELECT number AS FR', required columns: 'number')|` +
`(?:Unknown expression identifier 'number' in scope SELECT number AS FR))`
`(?:Unknown expression identifier ('|\`)number('|\`) in scope SELECT number AS FR))`
await expectAsync(
client.query({
query: 'SELECT number FR',
Expand All @@ -37,7 +38,7 @@ describe('ClickHouse server errors parsing', () => {
const dbName = getTestDatabaseName()
const errorMessagePattern =
`((?:^Table ${dbName}.unknown_table does not exist.*)|` +
`(?:Unknown table expression identifier 'unknown_table' in scope))`
`(?:Unknown table expression identifier ('|\`)unknown_table('|\`) in scope))`
await expectAsync(
client.query({
query: 'SELECT * FROM unknown_table',
Expand Down
56 changes: 56 additions & 0 deletions packages/client-common/__tests__/unit/parse_column_types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { parseFixedStringType } from '../../src/parse'

describe('Columns types parser', () => {
describe('FixedString', () => {
it('should parse FixedString', async () => {
const args: [string, number][] = [
['FixedString(1)', 1],
['FixedString(42)', 42],
['FixedString(100)', 100],
['FixedString(32768)', 32768],
]
args.forEach(([columnType, sizeBytes]) => {
const result = parseFixedStringType({
columnType,
sourceType: columnType,
})
expect(result)
.withContext(
`Expected ${columnType} to be parsed as a FixedString with size ${sizeBytes}`,
)
.toEqual({ type: 'FixedString', sizeBytes, sourceType: columnType })
})
})

it('should throw on invalid FixedString type', async () => {
const args: [string][] = [
['FixedString'],
['FixedString('],
['FixedString()'],
['String'],
]
args.forEach(([columnType]) => {
expect(() =>
parseFixedStringType({ columnType, sourceType: columnType }),
)
.withContext(`Expected ${columnType} to throw`)
.toThrowError('Invalid FixedString type')
})
})

it('should throw on invalid FixedString size', async () => {
const args: [string][] = [
['FixedString(0)'],
['FixedString(x)'],
[`FixedString(')`],
]
args.forEach(([columnType]) => {
expect(() =>
parseFixedStringType({ columnType, sourceType: columnType }),
)
.withContext(`Expected ${columnType} to throw`)
.toThrowError('Invalid FixedString size in bytes')
})
})
})
})
Loading
Loading