activerecord-duckdb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.fasterer.yml +31 -0
- data/.rspec +3 -0
- data/.rubocop.yml +105 -0
- data/.simplecov +18 -0
- data/.tool-versions +1 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +131 -0
- data/LICENSE.txt +21 -0
- data/README.md +168 -0
- data/Rakefile +19 -0
- data/TODO.md +16 -0
- data/docs/TABLE_DEFINITION.md +322 -0
- data/lib/active_record/connection_adapters/duckdb/column.rb +67 -0
- data/lib/active_record/connection_adapters/duckdb/database_limits.rb +54 -0
- data/lib/active_record/connection_adapters/duckdb/database_statements.rb +303 -0
- data/lib/active_record/connection_adapters/duckdb/database_tasks.rb +22 -0
- data/lib/active_record/connection_adapters/duckdb/quoting.rb +59 -0
- data/lib/active_record/connection_adapters/duckdb/schema_creation.rb +43 -0
- data/lib/active_record/connection_adapters/duckdb/schema_definitions.rb +145 -0
- data/lib/active_record/connection_adapters/duckdb/schema_dumper.rb +165 -0
- data/lib/active_record/connection_adapters/duckdb/schema_statements.rb +599 -0
- data/lib/active_record/connection_adapters/duckdb_adapter.rb +436 -0
- data/lib/active_record/tasks/duckdb_database_tasks.rb +170 -0
- data/lib/activerecord/duckdb/version.rb +11 -0
- data/lib/activerecord-duckdb.rb +60 -0
- data/sig/activerecord/duckdb.rbs +6 -0
- data/tmp/.gitkeep +0 -0
- metadata +107 -0
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Duckdb
|
6
|
+
# DuckDB-specific schema dumper functionality for generating schema.rb files
|
7
|
+
# Provides methods to properly dump DuckDB-specific column types and constraints
|
8
|
+
module SchemaDumper
|
9
|
+
# Generates column specification for schema dumping
|
10
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to generate spec for
|
11
|
+
# @return [Array] Array containing column type and options hash
|
12
|
+
def column_spec(column)
|
13
|
+
column_type = schema_type(column)
|
14
|
+
column_options = prepare_column_options(column)
|
15
|
+
# For any column with sequence defaults, include them in column options
|
16
|
+
if column.respond_to?(:default_function) && column.default_function&.include?('nextval(')
|
17
|
+
# Include the sequence function as column default
|
18
|
+
column_options[:default] = "-> { \"#{column.default_function}\" }"
|
19
|
+
end
|
20
|
+
[column_type, column_options]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Maps DuckDB SQL types to ActiveRecord schema types
|
24
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to map
|
25
|
+
# @return [Symbol] The ActiveRecord schema type symbol
|
26
|
+
def schema_type(column)
|
27
|
+
case column.sql_type.to_s.upcase
|
28
|
+
when /^BIGINT$/i
|
29
|
+
:bigint
|
30
|
+
when /^INTEGER$/i
|
31
|
+
:integer
|
32
|
+
when /^VARCHAR$/i, /^VARCHAR\(\d+\)$/i
|
33
|
+
:string
|
34
|
+
when /^TEXT$/i
|
35
|
+
:text
|
36
|
+
when /^TIMESTAMP$/i
|
37
|
+
:datetime
|
38
|
+
when /^BOOLEAN$/i
|
39
|
+
:boolean
|
40
|
+
when /^UUID$/i
|
41
|
+
:uuid
|
42
|
+
when /^DECIMAL\((\d+),(\d+)\)$/i
|
43
|
+
:decimal
|
44
|
+
when /^BLOB$/i
|
45
|
+
:binary
|
46
|
+
when /^REAL$/i, /^DOUBLE$/i
|
47
|
+
:float
|
48
|
+
when /^DATE$/i
|
49
|
+
:date
|
50
|
+
when /^TIME$/i
|
51
|
+
:time
|
52
|
+
else
|
53
|
+
column.type
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Determines if a column uses the default primary key behavior
|
58
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to check
|
59
|
+
# @return [Boolean] true if column uses default primary key behavior
|
60
|
+
def default_primary_key?(column)
|
61
|
+
# Never treat sequence-based primary keys as having default behavior
|
62
|
+
return false if column.respond_to?(:default_function) && column.default_function&.include?('nextval(')
|
63
|
+
|
64
|
+
# Only consider it a default primary key if it's bigint without sequences
|
65
|
+
schema_type(column) == :bigint
|
66
|
+
end
|
67
|
+
|
68
|
+
# Determines if a primary key column requires explicit default inclusion
|
69
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to check
|
70
|
+
# @return [Boolean] true if column requires explicit default in schema
|
71
|
+
def explicit_primary_key_default?(column)
|
72
|
+
# Return true for any column with sequence defaults to force explicit inclusion
|
73
|
+
column.respond_to?(:default_function) && column.default_function&.include?('nextval(')
|
74
|
+
end
|
75
|
+
|
76
|
+
# Prepares column options hash for schema dumping
|
77
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to prepare options for
|
78
|
+
# @return [Hash] Hash of column options for schema dumping
|
79
|
+
def prepare_column_options(column)
|
80
|
+
spec = {}
|
81
|
+
|
82
|
+
# Add limit only for string types and when meaningful
|
83
|
+
if (limit = schema_limit(column))
|
84
|
+
spec[:limit] = limit
|
85
|
+
end
|
86
|
+
|
87
|
+
# Add precision only for numeric types and when meaningful (not nil/zero)
|
88
|
+
if (precision = schema_precision(column))
|
89
|
+
spec[:precision] = precision
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add scale only for numeric types and when meaningful
|
93
|
+
if (scale = schema_scale(column))
|
94
|
+
spec[:scale] = scale
|
95
|
+
end
|
96
|
+
|
97
|
+
# Add null constraint
|
98
|
+
spec[:null] = false unless column.null
|
99
|
+
|
100
|
+
# Add default value if present and not a function (sequences handled in column_spec)
|
101
|
+
if (default = schema_default(column))
|
102
|
+
spec[:default] = default
|
103
|
+
end
|
104
|
+
|
105
|
+
# Add comment if present
|
106
|
+
spec[:comment] = column.comment.inspect if column.comment.present?
|
107
|
+
spec = spec.compact
|
108
|
+
spec
|
109
|
+
end
|
110
|
+
|
111
|
+
# Extracts limit option for schema dumping
|
112
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to extract limit from
|
113
|
+
# @return [Integer, nil] The column limit or nil if not applicable
|
114
|
+
def schema_limit(column)
|
115
|
+
return column.limit if column.limit && column.type == :string
|
116
|
+
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
# Extracts precision option for schema dumping
|
121
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to extract precision from
|
122
|
+
# @return [Integer, nil] The column precision or nil if not applicable
|
123
|
+
def schema_precision(column)
|
124
|
+
return nil unless %i[decimal float numeric real].include?(column.type)
|
125
|
+
return nil unless column.precision&.positive?
|
126
|
+
|
127
|
+
column.precision
|
128
|
+
end
|
129
|
+
|
130
|
+
# Extracts scale option for schema dumping
|
131
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to extract scale from
|
132
|
+
# @return [Integer, nil] The column scale or nil if not applicable
|
133
|
+
def schema_scale(column)
|
134
|
+
return nil unless %i[decimal float numeric real].include?(column.type)
|
135
|
+
return nil unless column.scale && column.scale >= 0
|
136
|
+
|
137
|
+
column.scale
|
138
|
+
end
|
139
|
+
|
140
|
+
# Extracts and formats default value for schema dumping
|
141
|
+
# @param column [ActiveRecord::ConnectionAdapters::Column] The column to extract default from
|
142
|
+
# @return [Object, nil] The formatted default value or nil if no default
|
143
|
+
def schema_default(column)
|
144
|
+
return nil if column.respond_to?(:default_function) && column.default_function
|
145
|
+
|
146
|
+
case column.default
|
147
|
+
when nil
|
148
|
+
nil
|
149
|
+
when true, false, Numeric
|
150
|
+
column.default
|
151
|
+
when String
|
152
|
+
# Handle DuckDB's boolean format: CAST('t' AS BOOLEAN) or CAST('f' AS BOOLEAN)
|
153
|
+
if column.default.match?(/^CAST\('([tf])' AS BOOLEAN\)$/i)
|
154
|
+
column.default.match(/^CAST\('([tf])' AS BOOLEAN\)$/i)[1].downcase == 't'
|
155
|
+
else
|
156
|
+
column.default.inspect
|
157
|
+
end
|
158
|
+
else
|
159
|
+
column.default.inspect
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|