NAME
DBIO::ClickHouse::Test::Conformance - Answers the shared conformance case table for the ClickHouse driver
VERSION
version 0.900004
SYNOPSIS
use DBIO::ClickHouse::Test::Conformance;
DBIO::ClickHouse::Test::Conformance->run('DDL');
done_testing;
DESCRIPTION
The driver's half of the shared case table of DBIO::Test::Conformance (core ADR 0060). Core owns the cases and the runner; this class says how to reach a ClickHouse server, how to install a declaration into it through DBIO::ClickHouse::Deploy, and how to read a fact back through DBIO::ClickHouse::Introspect.
The server is the one DBIO_TEST_CLICKHOUSE_DSN, DBIO_TEST_CLICKHOUSE_DBUSER and DBIO_TEST_CLICKHOUSE_DBPASS name -- the variables of DBIO::ClickHouse::Test. ClickHouse has no embedded mode, so without the DSN (or without a MySQL-wire DBD) only the declarations are checked. A run needs an account that may create and drop databases: "diff" in DBIO::ClickHouse::Deploy builds its target in a scratch one.
The namespace of a case is a database of its own. Every case gets a database dbio_cf_<pid>_<n>, created by "conformance_connect" over a connection of its own; the DSN of the case names it, so the diff -- which compares the whole of the current database -- sees nothing but the case. "conformance_namespace" refuses a database that is not empty, and "conformance_cleanup" drops a database only when this run created it. Nothing else on the server is touched.
The class names no storage class beyond what the ClickHouse component pins.
METHODS
conformance_live_available
True when DBIO_TEST_CLICKHOUSE_DSN is set and the DBD it names loads (DBD::mysql or DBD::MariaDB).
conformance_connect
Creates the database of the case, loads the ClickHouse schema component into the case's schema class and connects to that database. The component pins the storage; the DSN only says which DBD reaches it.
conformance_namespace
Returns the name of the case's database as the token for "conformance_cleanup", after checking that it holds no table and no view. A database that is not empty ends the case.
conformance_install
Installs the declaration with "install" in DBIO::ClickHouse::Deploy, the driver's public entry point. Not $schema->deploy: that one carps on a failed statement and carries on, which would turn a refusal into a warning.
A source named database.table (ddl-qualified-source-name) is made in a database that is not the case's, so the class takes care of it: a table of that name that is on the server already is not the run's to use or drop, and ends the case; one that is not is marked, and "conformance_cleanup" drops it. The database itself is never created or dropped.
conformance_cleanup
Drops the database of the token when "conformance_connect" of this run created it, the tables "conformance_install" marked in other databases, and disconnects. Nothing else is dropped: without a mark no other database is touched. The drops go through a connection of their own.
conformance_diff_is_empty
True when "diff" in DBIO::ClickHouse::Deploy of the installed database against the same declaration has no changes.
conformance_upgrade
Connects the target schema class to the database of the installed one and runs "upgrade" in DBIO::ClickHouse::Deploy -- the public entry point -- through it. The target class takes over the namespace (and the duty to drop it) of the installed one, since the runner hands only the returned schema to "conformance_cleanup". The upgrade runs with allow_destructive => 1: the cases of the table are about what the diff does (a dropped column, a changed type); the guard that holds a destructive diff back has its own cases (diff-deploy-destructive-guard, run through "conformance_deploy"). A refusal of the diff is thrown from here as it came; the database then stays with the installed schema, whose cleanup drops it.
conformance_deploy
The deploy object over the target schema, for the deploy cases of the Diff list (table version 4): the target class is connected to the database of the installed one exactly as "conformance_upgrade" connects it, and takes over its namespace and the duty to drop it. Returns DBIO::ClickHouse::Deploy->new(schema => $to); the runner calls apply and upgrade on it.
conformance_answered_through
The table versions the answers were written against, per area: { DDL => 2, Diff => 4, Bind => 1 }. The DDL and Diff lists are answered by this class; the Bind cases (t/62) run unrefused except as "Bind" says. Version 4 of the Diff list adds the deploy cases (diff-deploy-*): they run through "conformance_deploy", which hands the runner the DBIO::ClickHouse::Deploy object, so apply and upgrade are the base class's.
conformance_refusals
ClickHouse has no foreign key, no enforced UNIQUE and no identity column; an is_auto_increment column that is not a UUID is refused too (ddl-auto-increment, diff-auto-increment-kept, bind-lob-row-operations; the engine has no AUTO_INCREMENT, https://clickhouse.com/docs/sql-reference/statements/create/table), and so is a bit with a size (ddl-type-size-bit: no BIT type, https://clickhouse.com/docs/sql-reference/data-types). ddl-type-size-char, diff-column-type-change and bind-numeric-exact are declared deviations with the reason in the declaration. A case that asks for one is a server refusal, thrown by "What install_ddl refuses" in DBIO::ClickHouse::DDL before the first statement and naming table and column -- never a table built without the constraint. The reason is the engine's own documentation: https://clickhouse.com/docs/sql-reference/statements/create/table (the CREATE TABLE grammar has no foreign key and no unique clause) and https://clickhouse.com/docs/guides/developer/deduplication (ClickHouse does not check for an existing primary key before inserting; ReplacingMergeTree deduplicates at merge time, not on insert).
ddl-qualified-source-name writes the table t into the database other (table('other.t')). The driver creates no database, and the case does not either, so the answer depends on the server: where a database other exists, the table is made in it and the case runs through, read back by "conformance_fact"; where it does not, "install" in DBIO::ClickHouse::Deploy refuses, naming table and database, before its first statement, and the case is declared a server refusal. Which of the two holds is asked of the server (_database_exists), read only.
The Diff cases that declare a foreign key or a unique constraint (in from or to) are refused the same way. The refusal of a table names every constraint it cannot build in one text, each with the table's columns; that is what lets diff-constraint-changed (subject child.code, a unique constraint only the target declares, the installed from having the foreign key alone) meet the refusal convention. The column is named as context of the table, not because the driver knew the target. diff-column-added-not-null-populated and diff-not-null-over-nulls are server refusals of "_check_before_apply" in DBIO::ClickHouse::Deploy, thrown before the first statement: the server would fill the rows with the type's zero value, or switch the column's metadata and then fail it in the background mutation (https://clickhouse.com/docs/sql-reference/statements/alter/column).
conformance_fact
Reads one fact through DBIO::ClickHouse::Introspect, of the database of the case; the vocabulary is the one of "Facts" in DBIO::Test::Conformance. table_in_namespace asks the namespace named, a database. The facts of constraints are answered as the engine has them: no foreign key, no unique constraint and no index but the skipping indexes (DBIO::ClickHouse::Introspect).
Bind
The Bind list needs no refusal of this class beyond one case: a value goes through the column by every operation of the ResultSet API and either comes back as written or the case fails. bind-numeric-exact is a declared deviation (Decimal is exact but reads back without trailing zeros). bind-lob-row-operations needs an auto-increment key, which the DDL refuses (see "conformance_refusals"); it is declared a server refusal, but the runner still fails it because the refusal names the column id and the subject core gives the case is body.
Two cases pass because the driver closes what the server does silently, not because they are declared: bind-empty-string (a string that is not a number at a numeric column is refused by "_prep_for_execute" in DBIO::ClickHouse::Storage before it is sent -- the server would store NULL), and bind-binary and bind-lob-empty-and-large (a 0x1A byte is bound through a marker by "_render_bind" in DBIO::ClickHouse::SQLMaker -- the DBD escapes it as \Z, which ClickHouse keeps as two characters).
SEE ALSO
DBIO::Test::Conformance - the runner and the contract of the seams
DBIO::Test::Conformance::Cases::DDL - the DDL case list
DBIO::ClickHouse::Deploy - install and diff, as the cases reach them
DBIO::ClickHouse::Introspect - what the facts are read through
AUTHOR
DBIO Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.