Skip to content
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
36 changes: 0 additions & 36 deletions .github/workflows/publish-client-common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,42 +53,6 @@ env:
NPM_CONFIG_FETCH_TIMEOUT: "600000"

jobs:
head:
name: "Publish @clickhouse/client-common (head)"
if: github.ref == 'refs/heads/release' && github.event_name == 'push'
runs-on: ubuntu-latest
environment: npm-publish
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 24
registry-url: "https://registry.npmjs.org"

- name: Install dependencies
run: npm ci

- name: Set head pre-release version
run: |
BASE_VERSION=$(node -p "require('./packages/client-common/package.json').version")
HEAD_VERSION="${BASE_VERSION}-head.${GITHUB_SHA::7}.${GITHUB_RUN_ATTEMPT}"
echo "Setting version to: $HEAD_VERSION"
npm --workspace @clickhouse/client-common version --no-git-tag-version "$HEAD_VERSION"
echo "export default \"$HEAD_VERSION\";" > packages/client-common/src/version.ts

- name: Build the package
run: npm --workspace @clickhouse/client-common run build

- name: Publish @clickhouse/client-common with head tag
run: |
npm --workspace @clickhouse/client-common publish \
--access public \
--provenance \
--tag head

publish:
name: "Publish @clickhouse/client-common (deprecated)"
if: github.ref == 'refs/heads/release' && github.event_name == 'workflow_dispatch'
Expand Down
30 changes: 19 additions & 11 deletions package-lock.json

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

16 changes: 8 additions & 8 deletions skills/clickhouse-js-node-rowbinary/package-lock.json

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

2 changes: 1 addition & 1 deletion skills/clickhouse-js-node-rowbinary/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"@clickhouse/datatype-parser": "^0.1.2"
},
"devDependencies": {
"@types/node": "^26.0.0",
"@types/node": "25.9.3",
"typescript": "^6.0.3",
"vitest": "^4.1.9"
}
Expand Down
12 changes: 8 additions & 4 deletions tests/clickhouse-test-runner/__tests__/tsv-serialize.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { parseDataType } from "@clickhouse/datatype-parser";
import { formatUUID } from "@clickhouse/rowbinary/uuid";
import { formatUUID } from "@clickhouse/rowbinary/readers/uuid";
import { renderValue, compileRowRenderers } from "../src/tsv-serialize.js";

/**
Expand Down Expand Up @@ -100,9 +100,13 @@ describe("renderValue — top level (escaped, unquoted)", () => {
expect(render("IPv6", ipv6)).toBe("::1");
});

it("enum maps the wire integer to its name", () => {
expect(render("Enum8('x' = 1, 'y' = 2)", 2)).toBe("y");
expect(render("Enum16('a' = 10, 'b' = -20)", -20)).toBe("b");
it("renders the enum name the reader already resolved", () => {
// @clickhouse/rowbinary resolves Enum8/16 to the NAME (not the wire
// integer), so renderValue receives the name string and renders it
// stringish: escaped + unquoted at top level, single-quoted when nested.
expect(render("Enum8('x' = 1, 'y' = 2)", "y")).toBe("y");
expect(render("Enum16('a' = 10, 'b' = -20)", "b")).toBe("b");
expect(render("Enum8('x' = 1, 'y' = 2)", "y", true)).toBe("'y'");
});

it("NULL is backslash-N at top level", () => {
Expand Down
6 changes: 4 additions & 2 deletions tests/clickhouse-test-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
},
"scripts": {
"pack": "true",
"build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/main.js",
"build": "npm run build:rowbinary && rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/main.js",
"//build:rowbinary": "The in-repo @clickhouse/rowbinary skill is linked via file: (not a workspace, so it stays out of the root install/lint/test sweeps and keeps its own standalone CI). Its dist is gitignored, so build it here — the test runner is the last workspace built, so packages/* (datatype-parser) are already compiled when the skill's tsc resolves them.",
"build:rowbinary": "tsc -p ../../skills/clickhouse-js-node-rowbinary/tsconfig.build.json",
"typecheck": "tsc --noEmit",
Comment on lines +15 to 18
"lint": "eslint --max-warnings=0 .",
"lint:fix": "eslint . --fix",
"test": "vitest run --root ."
},
"dependencies": {
"@clickhouse/client": "*",
"@clickhouse/rowbinary": "^0.1.2"
"@clickhouse/rowbinary": "file:../../skills/clickhouse-js-node-rowbinary"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
Expand Down
4 changes: 2 additions & 2 deletions tests/clickhouse-test-runner/src/backends/rowbinary.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { compileRowBinaryWithNamesAndTypes } from "@clickhouse/rowbinary/rowBinaryWithNamesAndTypes";
import { Cursor } from "@clickhouse/rowbinary/core";
import { compileRowBinaryWithNamesAndTypes } from "@clickhouse/rowbinary/readers/rowBinaryWithNamesAndTypes";
import { Cursor } from "@clickhouse/rowbinary/readers/core";
Comment on lines +1 to +2
import { compileRowRenderers } from "../tsv-serialize.js";
import { appendLog } from "../log.js";
import {
Expand Down
29 changes: 11 additions & 18 deletions tests/clickhouse-test-runner/src/tsv-serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
* The renderer is TYPE-DIRECTED: it walks the column's parsed data-type AST
* (from `@clickhouse/datatype-parser`, the same AST the parser folds into
* readers) alongside the decoded value, because the JS value alone is
* insufficient to reproduce ClickHouse's text — e.g. an `Enum8` decodes to its
* underlying integer but TSV prints the NAME, which lives only in the type.
* insufficient to reproduce ClickHouse's text — e.g. a `DateTime64(P)` decodes
* to `[Date, nanoseconds]` but the sub-second precision `P` that decides how
* many fractional digits to print lives only in the type.
*
* Two text contexts, mirroring ClickHouse's `serializeTextEscaped` (top level)
* vs `serializeTextQuoted` (inside Array/Tuple/Map):
Expand All @@ -33,10 +34,10 @@ import {
NodeKind,
type Node,
} from "@clickhouse/datatype-parser";
import { formatDecimal } from "@clickhouse/rowbinary/decimals";
import { formatTime, formatTime64 } from "@clickhouse/rowbinary/time";
import { formatUUID } from "@clickhouse/rowbinary/uuid";
import { formatIPv4, formatIPv6 } from "@clickhouse/rowbinary/ip";
import { formatDecimal } from "@clickhouse/rowbinary/readers/decimals";
import { formatTime, formatTime64 } from "@clickhouse/rowbinary/readers/time";
import { formatUUID } from "@clickhouse/rowbinary/readers/uuid";
import { formatIPv4, formatIPv6 } from "@clickhouse/rowbinary/readers/ip";
Comment on lines +37 to +40

/** Thrown when a column type has no TSV renderer yet (see module note). */
export class TSVRenderError extends Error {
Expand Down Expand Up @@ -161,17 +162,6 @@ function renderPointArray(
return `[${(value as unknown[]).map(renderElem).join(",")}]`;
}

/** Map a decoded enum integer to its name via the explicit `'name' = value` pairs in the type. */
function enumName(node: Node, value: unknown): string {
const v = BigInt(value as number);
for (const ev of node.values) {
if (ev.value === v) return ev.name;
}
throw new TSVRenderError(
`enum value ${String(value)} not found in ${node.name}`,
);
}

function requireArg(node: Node, index: number): Node {
const arg = node.arguments[index];
if (arg === undefined) {
Expand Down Expand Up @@ -201,7 +191,10 @@ export function renderValue(
if (value === null || value === undefined) return nested ? "NULL" : "\\N";

if (node.kind === NodeKind.EnumDataType) {
return renderStringish(enumName(node, value), nested);
// @clickhouse/rowbinary's enum readers already resolve the underlying
// integer to its NAME via the type's value→name map, so render that string
// directly (ClickHouse TSV prints the enum name, not the integer).
return renderStringish(String(value), nested);
}
if (node.kind === NodeKind.TupleDataType) {
return renderTuple(node, value);
Expand Down