Module: ActiveRecord::ConnectionAdapters::Duckdb::SchemaDumper

Included in:
ActiveRecord::ConnectionAdapters::DuckdbAdapter
Defined in:
lib/active_record/connection_adapters/duckdb/schema_dumper.rb

Overview

DuckDB-specific schema dumper functionality for generating schema.rb files Provides methods to properly dump DuckDB-specific column types and constraints

Instance Method Summary collapse

Instance Method Details

#column_spec(column) ⇒ Array

Generates column specification for schema dumping



12
13
14
15
16
17
18
19
20
21
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 12

def column_spec(column)
  column_type = schema_type(column)
  column_options = prepare_column_options(column)
  # For any column with sequence defaults, include them in column options
  if column.respond_to?(:default_function) && column.default_function&.include?('nextval(')
    # Include the sequence function as column default
    column_options[:default] = "-> { \"#{column.default_function}\" }"
  end
  [column_type, column_options]
end

#default_primary_key?(column) ⇒ Boolean

Determines if a column uses the default primary key behavior



60
61
62
63
64
65
66
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 60

def default_primary_key?(column)
  # Never treat sequence-based primary keys as having default behavior
  return false if column.respond_to?(:default_function) && column.default_function&.include?('nextval(')

  # Only consider it a default primary key if it's bigint without sequences
  schema_type(column) == :bigint
end

#explicit_primary_key_default?(column) ⇒ Boolean

Determines if a primary key column requires explicit default inclusion



71
72
73
74
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 71

def explicit_primary_key_default?(column)
  # Return true for any column with sequence defaults to force explicit inclusion
  column.respond_to?(:default_function) && column.default_function&.include?('nextval(')
end

#prepare_column_options(column) ⇒ Hash

Prepares column options hash for schema dumping



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 79

def prepare_column_options(column)
  spec = {}

  # Add limit only for string types and when meaningful
  if (limit = schema_limit(column))
    spec[:limit] = limit
  end

  # Add precision only for numeric types and when meaningful (not nil/zero)
  if (precision = schema_precision(column))
    spec[:precision] = precision
  end

  # Add scale only for numeric types and when meaningful
  if (scale = schema_scale(column))
    spec[:scale] = scale
  end

  # Add null constraint
  spec[:null] = false unless column.null

  # Add default value if present and not a function (sequences handled in column_spec)
  if (default = schema_default(column))
    spec[:default] = default
  end

  # Add comment if present
  spec[:comment] = column.comment.inspect if column.comment.present?
  spec = spec.compact
  spec
end

#schema_default(column) ⇒ Object?

Extracts and formats default value for schema dumping



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 143

def schema_default(column)
  return nil if column.respond_to?(:default_function) && column.default_function

  case column.default
  when nil
    nil
  when true, false, Numeric
    column.default
  when String
    # Handle DuckDB's boolean format: CAST('t' AS BOOLEAN) or CAST('f' AS BOOLEAN)
    if column.default.match?(/^CAST\('([tf])' AS BOOLEAN\)$/i)
      column.default.match(/^CAST\('([tf])' AS BOOLEAN\)$/i)[1].downcase == 't'
    else
      column.default.inspect
    end
  else
    column.default.inspect
  end
end

#schema_limit(column) ⇒ Integer?

Extracts limit option for schema dumping



114
115
116
117
118
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 114

def schema_limit(column)
  return column.limit if column.limit && column.type == :string

  nil
end

#schema_precision(column) ⇒ Integer?

Extracts precision option for schema dumping



123
124
125
126
127
128
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 123

def schema_precision(column)
  return nil unless i[decimal float numeric real].include?(column.type)
  return nil unless column.precision&.positive?

  column.precision
end

#schema_scale(column) ⇒ Integer?

Extracts scale option for schema dumping



133
134
135
136
137
138
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 133

def schema_scale(column)
  return nil unless i[decimal float numeric real].include?(column.type)
  return nil unless column.scale && column.scale >= 0

  column.scale
end

#schema_type(column) ⇒ Symbol

Maps DuckDB SQL types to ActiveRecord schema types



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/active_record/connection_adapters/duckdb/schema_dumper.rb', line 26

def schema_type(column)
  case column.sql_type.to_s.upcase
  when /^BIGINT$/i
    :bigint
  when /^INTEGER$/i
    :integer
  when /^VARCHAR$/i, /^VARCHAR\(\d+\)$/i
    :string
  when /^TEXT$/i
    :text
  when /^TIMESTAMP$/i
    :datetime
  when /^BOOLEAN$/i
    :boolean
  when /^UUID$/i
    :uuid
  when /^DECIMAL\((\d+),(\d+)\)$/i
    :decimal
  when /^BLOB$/i
    :binary
  when /^REAL$/i, /^DOUBLE$/i
    :float
  when /^DATE$/i
    :date
  when /^TIME$/i
    :time
  else
    column.type
  end
end