ClickHouse/clickhouse-js

Cannot get enums to work with query_params

Looskie opened this issue · 2 comments

When I use query_params with an enum, it does not work.

Steps to reproduce

  1. Create a local ch server and run the table query
  2. Connect
  3. run the code example below

Expected behaviour

to work :D

Code example

await client.command({
  query: `INSERT INTO default.customers (id, customer_name, is_admin, created_at, random_enum, pi, card_number, uuid) VALUES ({v0: String}, {v1: String}, {v2: Boolean}, {v3: DateTime64}, {v4: Enum8}, {v5: Float32}, {v6: Int32}, {v7: UUID})`,
  query_params: {
      v0: 'd65d104b-8398-419a-9b7f-87d13872d5f7',
      v1: 'John Doe',
      v2: false,
      v3: new Date(),
      v4: 'A',
      v5: 3.14,
      v6: 123456789,
      v7: '123'
    }
})

Error log

Enum data type cannot be empty: While executing ValuesBlockInputFormat.

Configuration

Environment

  • Client version: 0.2.5
  • Language version: v20.5.0
  • OS: 14.0 (23A339)

ClickHouse server

  • ClickHouse Server version: 23.7.5
  • CREATE TABLE statements for tables involved:
CREATE TABLE IF NOT EXISTS default.customers
              (
                id String,
    customer_name String,
    is_admin Boolean,
    created_at DateTime64,
    random_enum Enum8('A' = 0, 'B' = 1, 'C' = 2),
    pi Float32,
    card_number Int32,
    uuid UUID
              )
              ENGINE = MergeTree()
              PRIMARY KEY id

I feel like I'm missing something so obvious

There are several ways to bind an enum.

Either send a string representation of your enum (A/B/C) and bind as a String, index (0/1/2) and bind as a UInt8, or provide an entire type Enum8('A' = 0, 'B' = 1, 'C' = 2) (it complaints about precisely that, Enum8 != Enum8('A' = 0, 'B' = 1, 'C' = 2)).

See also:

describe('Enum', () => {
it('should accept numeric enums in a parametrized query', async () => {
enum MyEnum {
foo = 0,
bar = 1,
qaz = 2,
}
const rs = await client.query({
query:
'SELECT * FROM system.numbers WHERE number = {filter: Int64} LIMIT 1',
format: 'TabSeparated',
query_params: {
filter: MyEnum.qaz, // translated to 2
},
})
const response = await rs.text()
expect(response).toBe('2\n')
})
it('should accept numeric enums in a parametrized query', async () => {
enum MyEnum {
foo = 'foo',
bar = 'bar',
}
const rs = await client.query({
query: 'SELECT concat({str1: String},{str2: String})',
format: 'TabSeparated',
query_params: {
str1: MyEnum.foo,
str2: MyEnum.bar,
},
})
const response = await rs.text()
expect(response).toBe('foobar\n')
})
// this one is taken from https://clickhouse.com/docs/en/sql-reference/data-types/enum/#usage-examples
it('should accept the entire enum definition in a parametrized query', async () => {
const rs = await client.query({
query: `SELECT toTypeName(CAST('a', {e: String}))`,
format: 'TabSeparated',
query_params: {
e: `Enum('a' = 1, 'b' = 2)`,
},
})
const response = await rs.text()
expect(response).toBe(`Enum8(\\'a\\' = 1, \\'b\\' = 2)\n`)
})
it('should provide error details when sending a request with missing parameter', async () => {
await expectAsync(
client.query({
query: `
SELECT * FROM system.numbers
WHERE number > {min_limit: UInt64} LIMIT 3
`,
})
).toBeRejectedWith(
jasmine.objectContaining({
message: jasmine.stringMatching(
// possible error messages here:
// (since 23.8+) Substitution `min_limit` is not set.
// (pre-23.8) Query parameter `min_limit` was not set
/^.+?`min_limit`.+?not set.*$/
),
code: '456',
type: 'UNKNOWN_QUERY_PARAMETER',
})
)
})
})

EDIT: this works.

  await client.command({
    query: `INSERT INTO default.customers (id, customer_name, is_admin, created_at, random_enum, pi, card_number, uuid)
            VALUES ({v0: String}, {v1: String}, {v2: Boolean}, {v3: DateTime64}, {v4: String}, {v5: Float32},
                    {v6: Int32}, {v7: UUID})`,
    query_params: {
      v0: 'd65d104b-8398-419a-9b7f-87d13872d5f7',
      v1: 'John Doe',
      v2: false,
      v3: new Date(),
      v4: 'A',
      v5: 3.14,
      v6: 123456789,
      v7: crypto.randomUUID(),
    },
  })

sweet, thank you so much for the quick answer ❤️