Skip to content

Commit 8e552d4

Browse files
fix: use [true, false] style for Array/Tuple/Map parameters (#475)
Co-authored-by: Serge Klochkov <[email protected]>
1 parent c83cde0 commit 8e552d4

File tree

3 files changed

+108
-7
lines changed

3 files changed

+108
-7
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88

99
- Made `TupleParam` constructor accept a readonly array to permit more usages. ([#465], [Malien])
1010

11+
## Bug fixes
12+
13+
- Fixed boolean value formatting in query parameters. Boolean values within `Array`, `Tuple`, and `Map` types are now correctly formatted as `TRUE`/`FALSE` instead of `1`/`0` to ensure proper type compatibility with ClickHouse. ([#475], [baseballyama])
14+
1115
[#465]: https://github.com/ClickHouse/clickhouse-js/pull/465
16+
[#475]: https://github.com/ClickHouse/clickhouse-js/pull/475
1217
[#478]: https://github.com/ClickHouse/clickhouse-js/pull/478
1318
[Malien]: https://github.com/Malien
19+
[baseballyama]: https://github.com/baseballyama
1420

1521
# 1.12.1
1622

@@ -212,7 +218,7 @@ A minor release to allow further investigation regarding uncaught error issues w
212218

213219
## New features
214220

215-
- Added optional `real_time_microseconds` field to the `ClickHouseSummary` interface (see https://github.com/ClickHouse/ClickHouse/pull/69032)
221+
- Added optional `real_time_microseconds` field to the `ClickHouseSummary` interface (see <https://github.com/ClickHouse/ClickHouse/pull/69032>)
216222

217223
## Bug fixes
218224

packages/client-common/__tests__/unit/format_query_params.test.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { formatQueryParams } from '@clickhouse/client-common'
1+
import { formatQueryParams, TupleParam } from '@clickhouse/client-common'
22

33
describe('formatQueryParams', () => {
44
it('formats null', () => {
@@ -158,4 +158,76 @@ describe('formatQueryParams', () => {
158158
}),
159159
).toBe("{'name':'custom','id':42,'params':{'refs':[44]}}")
160160
})
161+
162+
it('formats booleans in arrays as TRUE/FALSE', () => {
163+
expect(formatQueryParams({ value: [true, false] })).toBe('[TRUE,FALSE]')
164+
expect(formatQueryParams({ value: [true] })).toBe('[TRUE]')
165+
expect(formatQueryParams({ value: [false] })).toBe('[FALSE]')
166+
})
167+
168+
it('formats booleans in nested arrays as TRUE/FALSE', () => {
169+
expect(
170+
formatQueryParams({
171+
value: [
172+
[true, false],
173+
[false, true],
174+
],
175+
}),
176+
).toBe('[[TRUE,FALSE],[FALSE,TRUE]]')
177+
expect(
178+
formatQueryParams({
179+
value: [[[true]], [[false]]],
180+
}),
181+
).toBe('[[[TRUE]],[[FALSE]]]')
182+
})
183+
184+
it('formats booleans in arrays with mixed types', () => {
185+
expect(formatQueryParams({ value: [1, true, 'test', false, null] })).toBe(
186+
"[1,TRUE,'test',FALSE,NULL]",
187+
)
188+
})
189+
190+
it('formats booleans in tuples as TRUE/FALSE', () => {
191+
expect(
192+
formatQueryParams({
193+
value: new TupleParam([true, false]),
194+
}),
195+
).toBe('(TRUE,FALSE)')
196+
expect(
197+
formatQueryParams({
198+
value: new TupleParam([1, true, 'test', false]),
199+
}),
200+
).toBe("(1,TRUE,'test',FALSE)")
201+
})
202+
203+
it('formats booleans in nested tuples as TRUE/FALSE', () => {
204+
expect(
205+
formatQueryParams({
206+
value: new TupleParam([new TupleParam([true, false]), true]),
207+
}),
208+
).toBe('((TRUE,FALSE),TRUE)')
209+
})
210+
211+
it('formats booleans in objects (Maps) as TRUE/FALSE', () => {
212+
expect(
213+
formatQueryParams({
214+
value: {
215+
isActive: true,
216+
isDeleted: false,
217+
},
218+
}),
219+
).toBe("{'isActive':TRUE,'isDeleted':FALSE}")
220+
})
221+
222+
it('formats booleans in nested structures', () => {
223+
expect(
224+
formatQueryParams({
225+
value: {
226+
name: 'test',
227+
flags: [true, false],
228+
tuple: new TupleParam([false, true]),
229+
},
230+
}),
231+
).toBe("{'name':'test','flags':[TRUE,FALSE],'tuple':(FALSE,TRUE)}")
232+
})
161233
})

packages/client-common/src/data_formatter/format_query_params.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ export function formatQueryParams({
77
wrapStringInQuotes,
88
printNullAsKeyword,
99
}: FormatQueryParamsOptions): string {
10+
return formatQueryParamsInternal({
11+
value,
12+
wrapStringInQuotes,
13+
printNullAsKeyword,
14+
isInArrayOrTuple: false,
15+
})
16+
}
17+
18+
function formatQueryParamsInternal({
19+
value,
20+
wrapStringInQuotes,
21+
printNullAsKeyword,
22+
isInArrayOrTuple,
23+
}: FormatQueryParamsOptions & { isInArrayOrTuple: boolean }): string {
1024
if (value === null || value === undefined) {
1125
if (printNullAsKeyword) return 'NULL'
1226
return '\\N'
@@ -16,7 +30,12 @@ export function formatQueryParams({
1630
if (value === Number.NEGATIVE_INFINITY) return '-inf'
1731

1832
if (typeof value === 'number') return String(value)
19-
if (typeof value === 'boolean') return value ? '1' : '0'
33+
if (typeof value === 'boolean') {
34+
if (isInArrayOrTuple) {
35+
return value ? 'TRUE' : 'FALSE'
36+
}
37+
return value ? '1' : '0'
38+
}
2039
if (typeof value === 'string') {
2140
let result = ''
2241
for (let i = 0; i < value.length; i++) {
@@ -46,10 +65,11 @@ export function formatQueryParams({
4665
if (Array.isArray(value)) {
4766
return `[${value
4867
.map((v) =>
49-
formatQueryParams({
68+
formatQueryParamsInternal({
5069
value: v,
5170
wrapStringInQuotes: true,
5271
printNullAsKeyword: true,
72+
isInArrayOrTuple: true,
5373
}),
5474
)
5575
.join(',')}]`
@@ -70,10 +90,11 @@ export function formatQueryParams({
7090
if (value instanceof TupleParam) {
7191
return `(${value.values
7292
.map((v) =>
73-
formatQueryParams({
93+
formatQueryParamsInternal({
7494
value: v,
7595
wrapStringInQuotes: true,
7696
printNullAsKeyword: true,
97+
isInArrayOrTuple: true,
7798
}),
7899
)
79100
.join(',')})`
@@ -98,14 +119,16 @@ function formatObjectLikeParam(
98119
const formatted: string[] = []
99120
for (const [key, val] of entries) {
100121
formatted.push(
101-
`${formatQueryParams({
122+
`${formatQueryParamsInternal({
102123
value: key,
103124
wrapStringInQuotes: true,
104125
printNullAsKeyword: true,
105-
})}:${formatQueryParams({
126+
isInArrayOrTuple: true,
127+
})}:${formatQueryParamsInternal({
106128
value: val,
107129
wrapStringInQuotes: true,
108130
printNullAsKeyword: true,
131+
isInArrayOrTuple: true,
109132
})}`,
110133
)
111134
}

0 commit comments

Comments
 (0)