Module: ActiveRecord::ConnectionAdapters::ClickHouse::DatabaseStatements
- Included in:
- ActiveRecord::ConnectionAdapters::ClickHouseAdapter
- Defined in:
- lib/active_record/connection_adapters/clickhouse/database_statements.rb
Instance Method Summary collapse
- #build_explain_clause(options = []) ⇒ Object
-
#create_savepoint(_name = nil) ⇒ Object
Rails nests a SavepointTransaction inside any dirty transaction (e.g. the retry in create_or_find_by) even though supports_savepoints? is false.
-
#default_sequence_name(table_name, column_name) ⇒ Object
The "sequence" is the pk column itself; encode table.column so next_sequence_value can check it against the sorting key.
- #delete(arel, name = nil, binds = []) ⇒ Object
- #exec_rollback_to_savepoint(_name = nil) ⇒ Object
-
#explain(arel, binds = [], options = []) ⇒ Object
:nodoc:.
- #high_precision_current_timestamp ⇒ Object
-
#insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil) ⇒ Object
No INSERT ...
-
#insert_fixtures_set(fixture_set, tables_to_delete = []) ⇒ Object
The abstract version wraps bare DELETEs in a transaction; ClickHouse has neither, so fixtures load as TRUNCATE + batched INSERTs.
-
#insert_stream(table_name, rows, columns: nil) ⇒ Object
Bulk ingestion without materializing the batch: rows (any Enumerable of Hashes, lazy included) stream to the server as one chunked JSONCompactEachRow INSERT.
-
#next_sequence_value(sequence_name) ⇒ Object
Sequence names are usually the "table.column" this adapter encodes, but models may override sequence_name with a free-form label (Oracle legacy); those get integer ids on trust — the prefetch gate already vetted the table.
-
#prefetch_primary_key?(table_name = nil) ⇒ Boolean
No autoincrement and no INSERT ...
- #release_savepoint(_name = nil) ⇒ Object
-
#return_value_after_insert?(_column) ⇒ Boolean
Without RETURNING the server hands nothing back after an insert, whatever a column's default expression says; only the prefetched pk is knowable.
-
#update(arel, name = nil, binds = []) ⇒ Object
Mutations report no affected-row count (X-ClickHouse-Summary stays all zeros — probed 2026-07-13), so matching rows are counted just before mutating.
-
#with_request_settings(settings, &block) ⇒ Object
Scopes extra ClickHouse settings to every request made inside the block — the write-path counterpart of a relation's in-SQL SETTINGS clause.
-
#write_query?(sql) ⇒ Boolean
:nodoc:.
Instance Method Details
#build_explain_clause(options = []) ⇒ Object
60 61 62 63 64 65 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 60 def build_explain_clause( = []) variant = .first || :plan EXPLAIN_VARIANTS.fetch(variant.to_sym) do raise ArgumentError, "unknown EXPLAIN variant #{variant.inspect}; use #{EXPLAIN_VARIANTS.keys.inspect}" end end |
#create_savepoint(_name = nil) ⇒ Object
Rails nests a SavepointTransaction inside any dirty transaction (e.g. the retry in create_or_find_by) even though supports_savepoints? is false. Like begin/commit/rollback (PLAN.md §5 decision 4), the savepoint verbs are honest no-ops — nothing transactional exists to restore.
146 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 146 def create_savepoint(_name = nil); end |
#default_sequence_name(table_name, column_name) ⇒ Object
The "sequence" is the pk column itself; encode table.column so next_sequence_value can check it against the sorting key. Composite keys have no single column to generate.
78 79 80 81 82 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 78 def default_sequence_name(table_name, column_name) return nil if column_name.is_a?(Array) "#{table_name}.#{column_name}" end |
#delete(arel, name = nil, binds = []) ⇒ Object
124 125 126 127 128 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 124 def delete(arel, name = nil, binds = []) matched_rows = mutation_target_count(arel, name) result = super matched_rows || result end |
#exec_rollback_to_savepoint(_name = nil) ⇒ Object
147 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 147 def exec_rollback_to_savepoint(_name = nil); end |
#explain(arel, binds = [], options = []) ⇒ Object
:nodoc:
54 55 56 57 58 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 54 def explain(arel, binds = [], = []) # :nodoc: sql = "#{build_explain_clause()} #{to_sql(arel, binds)}" result = select_all(sql, "EXPLAIN", binds) ([result.columns.join("\t")] + result.rows.map { |row| row.join("\t") }).join("\n") end |
#high_precision_current_timestamp ⇒ Object
138 139 140 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 138 def HIGH_PRECISION_CURRENT_TIMESTAMP end |
#insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil) ⇒ Object
No INSERT ... RETURNING in ClickHouse: the prefetched client-side id is the only post-insert-knowable value, so surface it aligned to the requested returning columns for _create_record's write-back. The signature is Rails' DatabaseStatements#insert contract.
108 109 110 111 112 113 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 108 def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil) # rubocop:disable Metrics/ParameterLists inserted_id = super(arel, name, pk, id_value, sequence_name, binds, returning: nil) return inserted_id if returning.nil? returning.map { |column| column.to_s == pk.to_s ? inserted_id : nil } end |
#insert_fixtures_set(fixture_set, tables_to_delete = []) ⇒ Object
The abstract version wraps bare DELETEs in a transaction; ClickHouse has neither, so fixtures load as TRUNCATE + batched INSERTs.
152 153 154 155 156 157 158 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 152 def insert_fixtures_set(fixture_set, tables_to_delete = []) statements = tables_to_delete.map { |table| build_truncate_statement(table) } statements += fixture_set.filter_map do |table_name, fixtures| build_fixture_sql(fixtures, table_name) unless fixtures.empty? end statements.each { |statement| execute(statement, "Fixtures Load") } end |
#insert_stream(table_name, rows, columns: nil) ⇒ Object
Bulk ingestion without materializing the batch: rows (any Enumerable of Hashes, lazy included) stream to the server as one chunked JSONCompactEachRow INSERT. Returns the server-reported written row count.
44 45 46 47 48 49 50 51 52 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 44 def insert_stream(table_name, rows, columns: nil) column_names = columns || stream_column_names(rows) sql = insert_stream_sql(table_name, column_names) lines = Enumerator.new do |yielder| rows.each { |row| yielder << encoded_stream_row(row, column_names) } end stream_lines(sql, "#{table_name} Stream Insert", lines) end |
#next_sequence_value(sequence_name) ⇒ Object
Sequence names are usually the "table.column" this adapter encodes, but models may override sequence_name with a free-form label (Oracle legacy); those get integer ids on trust — the prefetch gate already vetted the table. nil means a composite primary key, which Rails' prefetch path cannot populate.
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 88 def next_sequence_value(sequence_name) raise_ungeneratable_primary_key(sequence_name) if sequence_name.nil? table_name, column_name = sequence_name.to_s.split(".", 2) return generate_time_ordered_id unless column_name generatable_column, sql_type = generatable_primary_key(table_name) raise_ungeneratable_primary_key(sequence_name) unless generatable_column == column_name sql_type == "UUID" ? generate_uuid_v7 : generate_time_ordered_id end |
#prefetch_primary_key?(table_name = nil) ⇒ Boolean
No autoincrement and no INSERT ... RETURNING: primary keys are generated client-side before the INSERT (the Oracle-adapter prefetch seam), but only for tables whose sorting key is a single generatable column — Rails' prefetch path cannot handle composite primary keys.
71 72 73 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 71 def prefetch_primary_key?(table_name = nil) !table_name.nil? && !generatable_primary_key(table_name).nil? end |
#release_savepoint(_name = nil) ⇒ Object
148 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 148 def release_savepoint(_name = nil); end |
#return_value_after_insert?(_column) ⇒ Boolean
Without RETURNING the server hands nothing back after an insert, whatever a column's default expression says; only the prefetched pk is knowable.
102 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 102 def return_value_after_insert?(_column) = false |
#update(arel, name = nil, binds = []) ⇒ Object
Mutations report no affected-row count (X-ClickHouse-Summary stays all zeros — probed 2026-07-13), so matching rows are counted just before mutating. Rows touched by concurrent writes between the two statements are not reflected.
118 119 120 121 122 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 118 def update(arel, name = nil, binds = []) matched_rows = mutation_target_count(arel, name) result = super matched_rows || result end |
#with_request_settings(settings, &block) ⇒ Object
Scopes extra ClickHouse settings to every request made inside the block — the write-path counterpart of a relation's in-SQL SETTINGS clause. Validated here, before with_raw_connection wraps errors into StatementInvalid.
32 33 34 35 36 37 38 39 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 32 def with_request_settings(settings, &block) settings.each_key do |name| unless /\A[a-zA-Z_][a-zA-Z0-9_]*\z/.match?(name.to_s) raise ArgumentError, "invalid ClickHouse setting name: #{name.inspect}" end end with_raw_connection { |raw_connection| raw_connection.with_request_settings(settings, &block) } end |
#write_query?(sql) ⇒ Boolean
:nodoc:
17 18 19 20 21 |
# File 'lib/active_record/connection_adapters/clickhouse/database_statements.rb', line 17 def write_query?(sql) # :nodoc: !READ_QUERY.match?(sql) rescue ArgumentError # non-UTF8 SQL, mirror the built-in adapters !READ_QUERY.match?(sql.b) end |