mimi-db 0.2.7 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/my_app.rb +3 -12
- data/examples/my_app_2.rb +20 -0
- data/examples/my_model.rb +13 -0
- data/lib/mimi/db/dictate/dsl.rb +2 -2
- data/lib/mimi/db/dictate/explorer.rb +34 -20
- data/lib/mimi/db/dictate/migrator.rb +35 -21
- data/lib/mimi/db/dictate/schema_definition.rb +61 -28
- data/lib/mimi/db/dictate/schema_diff.rb +1 -6
- data/lib/mimi/db/dictate/type_defaults.rb +167 -0
- data/lib/mimi/db/dictate.rb +15 -53
- data/lib/mimi/db/extensions/sequel-cockroachdb.rb +39 -0
- data/lib/mimi/db/extensions/sequel-database.rb +23 -0
- data/lib/mimi/db/extensions/sequel-postgres.rb +3 -0
- data/lib/mimi/db/extensions/sequel-sqlite.rb +3 -0
- data/lib/mimi/db/extensions.rb +14 -10
- data/lib/mimi/db/foreign_key.rb +9 -7
- data/lib/mimi/db/helpers.rb +39 -41
- data/lib/mimi/db/lock.rb +4 -0
- data/lib/mimi/db/model.rb +64 -0
- data/lib/mimi/db/version.rb +1 -1
- data/lib/mimi/db.rb +50 -23
- data/lib/tasks/db.rake +9 -1
- data/mimi-db.gemspec +1 -1
- metadata +13 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eec89d0a86280f91bedbfb6593261807397f6ac1
|
4
|
+
data.tar.gz: e769745b46e668134cace4e827bd434a4cae4287
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e330af2c6335bbab54cf6f8eb91416590adfa55dfbc5d3c2dc879b062e66cd67bd90e99b2d8b707c8f41724f12e0f4a182169572e49a94633d1ae168b012e56
|
7
|
+
data.tar.gz: aecc5b570e761ab14b13de6c19b50f65297ca46ac0d2be23404233f6b705add4c6ddaacfa4e95f97618d380c8b328c25bf2940c7c06015ecf70bf0cc2c97a518
|
data/examples/my_app.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
puts "Requiring mimi/db, mimi/logger"
|
3
4
|
require 'mimi/db'
|
4
5
|
require 'mimi/logger'
|
6
|
+
puts "DONE"
|
5
7
|
|
6
8
|
CONFIG = {
|
7
9
|
db_adapter: 'sqlite3',
|
@@ -12,18 +14,7 @@ CONFIG = {
|
|
12
14
|
Mimi::DB.configure(CONFIG)
|
13
15
|
Mimi::DB.start
|
14
16
|
|
15
|
-
|
16
|
-
field :id, as: :integer, primary_key: true, not_null: true, autoincrement: true
|
17
|
-
field :name, as: :string, limit: 64
|
18
|
-
field :code, as: :string, default: -> { random_code }
|
19
|
-
field :value, as: :decimal, precision: 10, scale: 3
|
20
|
-
|
21
|
-
index :name
|
22
|
-
|
23
|
-
def self.random_code
|
24
|
-
SecureRandom.hex(16)
|
25
|
-
end
|
26
|
-
end # class MyModel
|
17
|
+
require_relative 'my_model'
|
27
18
|
|
28
19
|
Mimi::DB.create_if_not_exist! # creates configured database
|
29
20
|
Mimi::DB.update_schema!
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
puts "Requiring mimi/db, mimi/logger"
|
4
|
+
require 'mimi/db'
|
5
|
+
require 'mimi/logger'
|
6
|
+
puts "DONE"
|
7
|
+
|
8
|
+
CONFIG = {
|
9
|
+
db_adapter: 'sqlite3',
|
10
|
+
db_database: '../tmp/my_app_db',
|
11
|
+
db_log_level: :debug
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
Mimi::DB.configure(CONFIG)
|
15
|
+
Mimi::DB.start
|
16
|
+
|
17
|
+
require_relative 'my_model'
|
18
|
+
|
19
|
+
require 'pp'
|
20
|
+
pp Mimi::DB.diff_schema
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class MyModel < Mimi::DB::Model
|
2
|
+
field :id, as: :integer, primary_key: true, not_null: true, auto_increment: true
|
3
|
+
|
4
|
+
field :name, as: :string, limit: 64
|
5
|
+
field :code, as: :blob, default: -> { random_code }
|
6
|
+
field :value, as: :decimal, precision: 10, scale: 3
|
7
|
+
|
8
|
+
index :name
|
9
|
+
|
10
|
+
def self.random_code
|
11
|
+
SecureRandom.hex(16)
|
12
|
+
end
|
13
|
+
end # class MyModel
|
data/lib/mimi/db/dictate/dsl.rb
CHANGED
@@ -52,8 +52,8 @@ module Mimi
|
|
52
52
|
# Sets up a default as a block/Proc
|
53
53
|
#
|
54
54
|
def field_setup_default(name, block)
|
55
|
-
before_validation
|
56
|
-
|
55
|
+
self.before_validation do
|
56
|
+
send :"#{name}=", instance_exec(&block) unless send(name.to_sym)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end # module DSL
|
@@ -9,10 +9,11 @@ module Mimi
|
|
9
9
|
#
|
10
10
|
# Returns nil if the DB table does not exist.
|
11
11
|
#
|
12
|
-
# @param table_name [String]
|
12
|
+
# @param table_name [String,Symbol]
|
13
13
|
# @return [Mimi::DB::Dictate::SchemaDefinition,nil]
|
14
14
|
#
|
15
15
|
def self.discover_schema(table_name)
|
16
|
+
table_name = table_name.to_sym
|
16
17
|
return nil unless connection.tables.include?(table_name)
|
17
18
|
sd = Mimi::DB::Dictate::SchemaDefinition.new(table_name)
|
18
19
|
discover_schema_columns(sd)
|
@@ -26,19 +27,20 @@ module Mimi
|
|
26
27
|
# @param schema_definition [Mimi::DB::Dictate::SchemaDefinition]
|
27
28
|
#
|
28
29
|
def self.discover_schema_columns(schema_definition)
|
29
|
-
columns = connection.
|
30
|
-
|
31
|
-
columns.each do |c|
|
30
|
+
columns = connection.schema(schema_definition.table_name).to_h
|
31
|
+
columns.each do |name, c|
|
32
32
|
params = {
|
33
|
-
as: c
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
as: c[:type],
|
34
|
+
type: c[:type],
|
35
|
+
size: c[:max_length],
|
36
|
+
primary_key: c[:primary_key],
|
37
|
+
auto_increment: c[:auto_increment], # FIXME: SQLite does not report autoincremented fields
|
38
|
+
null: c[:allow_null],
|
39
|
+
db_default: c[:default],
|
40
|
+
default: c[:default],
|
41
|
+
db_type: c[:db_type]
|
40
42
|
}
|
41
|
-
schema_definition.field(
|
43
|
+
schema_definition.field(name, params)
|
42
44
|
end
|
43
45
|
end
|
44
46
|
private_class_method :discover_schema_columns
|
@@ -49,23 +51,35 @@ module Mimi
|
|
49
51
|
# @param schema_definition [Mimi::DB::Dictate::SchemaDefinition]
|
50
52
|
#
|
51
53
|
def self.discover_schema_indexes(schema_definition)
|
52
|
-
indexes = connection.indexes(schema_definition.table_name)
|
53
|
-
pk =
|
54
|
-
indexes.each do |
|
54
|
+
indexes = connection.indexes(schema_definition.table_name).to_h
|
55
|
+
pk = discover_primary_key(schema_definition.table_name)
|
56
|
+
indexes.each do |idx_name, idx_data|
|
55
57
|
params = {
|
56
|
-
name:
|
57
|
-
primary_key:
|
58
|
-
unique:
|
58
|
+
name: idx_name,
|
59
|
+
primary_key: idx_data[:columns] == pk,
|
60
|
+
unique: idx_data[:unique]
|
59
61
|
}
|
60
|
-
schema_definition.index(
|
62
|
+
schema_definition.index(idx_data[:columns], params)
|
61
63
|
end
|
62
64
|
end
|
63
65
|
private_class_method :discover_schema_indexes
|
64
66
|
|
67
|
+
# Discovers primary key of an existing DB table
|
68
|
+
#
|
69
|
+
# @private
|
70
|
+
# @param table_name [String,Symbol]
|
71
|
+
#
|
72
|
+
# @return [Array<Symbol>]
|
73
|
+
#
|
74
|
+
def self.discover_primary_key(table_name)
|
75
|
+
s = connection.schema(table_name).to_h
|
76
|
+
s.keys.select { |name| s[name][:primary_key] }
|
77
|
+
end
|
78
|
+
|
65
79
|
# Returns ActiveRecord DB connection
|
66
80
|
#
|
67
81
|
def self.connection
|
68
|
-
|
82
|
+
Mimi::DB.connection
|
69
83
|
end
|
70
84
|
end # module Explorer
|
71
85
|
end # module Dictate
|
@@ -22,12 +22,12 @@ module Mimi
|
|
22
22
|
# @param options [Hash]
|
23
23
|
#
|
24
24
|
def initialize(table_name, options)
|
25
|
-
@table_name = table_name
|
25
|
+
@table_name = table_name.to_sym
|
26
26
|
@options = DEFAULTS.merge(options.dup)
|
27
|
-
@from_schema = self.class.db_schema_definition(table_name
|
28
|
-
@to_schema = Mimi::DB::Dictate.schema_definitions[table_name
|
27
|
+
@from_schema = self.class.db_schema_definition(@table_name)
|
28
|
+
@to_schema = Mimi::DB::Dictate.schema_definitions[@table_name]
|
29
29
|
if from_schema.nil? && to_schema.nil?
|
30
|
-
raise "Failed to migrate '#{table_name}', no DB or target schema found"
|
30
|
+
raise "Failed to migrate '#{@table_name}', no DB or target schema found"
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -36,7 +36,7 @@ module Mimi
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def db_connection
|
39
|
-
|
39
|
+
Mimi::DB.connection
|
40
40
|
end
|
41
41
|
|
42
42
|
# Returns true if the Migrator is configured to do a dry run (no actual changes to DB)
|
@@ -54,9 +54,11 @@ module Mimi
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def run!
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
db_ddl_transaction do
|
58
|
+
run_drop_table! if from_schema && to_schema.nil?
|
59
|
+
run_change_table! if from_schema && to_schema
|
60
|
+
run_create_table! if from_schema.nil? && to_schema
|
61
|
+
end
|
60
62
|
self.class.reset_db_schema_definition!(table_name)
|
61
63
|
end
|
62
64
|
|
@@ -110,15 +112,13 @@ module Mimi
|
|
110
112
|
def run_create_table!
|
111
113
|
columns = to_schema.columns.values
|
112
114
|
column_pk = to_schema.primary_key
|
113
|
-
params =
|
114
|
-
column_pk.params.select { |_, v| v }.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')
|
115
115
|
|
116
116
|
# issue CREATE TABLE with primary key field
|
117
117
|
logger.info "- CREATE TABLE: #{table_name}"
|
118
|
-
logger.info "-- add column: #{table_name}.#{column_pk
|
118
|
+
logger.info "-- add column: #{table_name}.#{column_pk}"
|
119
119
|
unless dry_run?
|
120
|
-
db_connection.create_table(table_name
|
121
|
-
|
120
|
+
db_connection.create_table(table_name) do |_|
|
121
|
+
column column_pk.name, column_pk.sequel_type, column_pk.to_sequel_params
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
@@ -130,28 +130,34 @@ module Mimi
|
|
130
130
|
def drop_column!(table_name, column_name)
|
131
131
|
logger.info "-- drop column: #{table_name}.#{column_name}"
|
132
132
|
return if dry_run? || !destructive?(:columns)
|
133
|
-
db_connection.
|
133
|
+
db_connection.drop_column(table_name, column_name)
|
134
134
|
end
|
135
135
|
|
136
136
|
def change_column!(table_name, column)
|
137
|
-
|
138
|
-
logger.info "-- change column: #{table_name}.#{column.name} (#{params})"
|
137
|
+
logger.info "-- change column: #{table_name}.#{column}"
|
139
138
|
return if dry_run?
|
140
|
-
db_connection.
|
139
|
+
db_connection.alter_table(table_name) do
|
140
|
+
set_column_type column.name, column.sequel_type, column.to_sequel_params.except(:default, :null)
|
141
|
+
set_column_default column.name, column.params[:default]
|
142
|
+
if column.to_sequel_params[:null]
|
143
|
+
set_column_allow_null column.name
|
144
|
+
else
|
145
|
+
set_column_not_null column.name
|
146
|
+
end
|
147
|
+
end
|
141
148
|
end
|
142
149
|
|
143
150
|
def add_column!(table_name, column)
|
144
|
-
|
145
|
-
logger.info "-- add column: #{table_name}.#{column.name} (#{params})"
|
151
|
+
logger.info "-- add column: #{table_name}.#{column}"
|
146
152
|
return if dry_run?
|
147
|
-
db_connection.add_column(table_name, column.name, column.
|
153
|
+
db_connection.add_column(table_name, column.name, column.sequel_type, column.to_sequel_params)
|
148
154
|
end
|
149
155
|
|
150
156
|
def drop_index!(table_name, idx)
|
151
157
|
idx_column_names = idx.columns.join(', ')
|
152
158
|
logger.info "-- drop index: #{idx.name} on #{table_name}(#{idx_column_names})"
|
153
159
|
return if dry_run?
|
154
|
-
db_connection.
|
160
|
+
db_connection.drop_index(table_name, idx.columns)
|
155
161
|
end
|
156
162
|
|
157
163
|
def add_index!(table_name, idx)
|
@@ -161,6 +167,14 @@ module Mimi
|
|
161
167
|
return if dry_run?
|
162
168
|
db_connection.add_index(table_name, idx.columns, idx.params)
|
163
169
|
end
|
170
|
+
|
171
|
+
def db_ddl_transaction(&_block)
|
172
|
+
supports_transactional_ddl =
|
173
|
+
db_connection.respond_to?(:supports_transactional_ddl?) &&
|
174
|
+
db_connection.supports_transactional_ddl?
|
175
|
+
return yield unless supports_transactional_ddl
|
176
|
+
db_connection.transaction { yield }
|
177
|
+
end
|
164
178
|
end # class Migrator
|
165
179
|
end # module Dictate
|
166
180
|
end # module DB
|
@@ -12,28 +12,48 @@ module Mimi
|
|
12
12
|
@indexes = []
|
13
13
|
end
|
14
14
|
|
15
|
+
# Declares a field
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# field :s1, as: :string
|
19
|
+
# field :s2, type: :string, size: 64
|
20
|
+
# field :v1, as: :decimal, size: 10 # precision: 10
|
21
|
+
# field :v2, as: :decimal, size: [10, 3] # precision: 10, scale: 3
|
22
|
+
#
|
23
|
+
#
|
15
24
|
def field(name, opts)
|
16
|
-
name = name.
|
25
|
+
name = name.to_sym
|
17
26
|
raise "Cannot redefine field :#{name}" if @columns[name]
|
18
|
-
|
27
|
+
opts_type = opts[:type] || opts[:as]
|
28
|
+
if primary_key && (opts[:primary_key] || opts_type == :primary_key)
|
19
29
|
raise "Cannot redefine primary key (:#{primary_key.name}) with :#{name}"
|
20
30
|
end
|
21
31
|
@columns[name] = Column.new(name, opts)
|
22
32
|
end
|
23
33
|
|
34
|
+
# Declares an index
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
# index :name
|
38
|
+
# index [:first_name, :last_name]
|
39
|
+
# index :ref_code, unique: true
|
40
|
+
#
|
41
|
+
# @param columns [String,Symbol,Array<String,Symbol>] columns to index on
|
42
|
+
# @param opts [Hash] index parameters (:unique, :name etc)
|
43
|
+
#
|
24
44
|
def index(columns, opts)
|
25
45
|
case columns
|
26
46
|
when String, Symbol
|
27
|
-
columns = [columns.
|
47
|
+
columns = [columns.to_sym]
|
28
48
|
when Array
|
29
49
|
unless columns.all? { |c| c.is_a?(String) || c.is_a?(Symbol) }
|
30
50
|
raise "Invalid column reference in index definition [#{columns}]"
|
31
51
|
end
|
32
|
-
columns = columns.map(&:
|
52
|
+
columns = columns.map(&:to_sym)
|
33
53
|
else
|
34
54
|
raise 'Invalid columns argument to .index'
|
35
55
|
end
|
36
|
-
if columns == [primary_key]
|
56
|
+
if columns == [primary_key.name]
|
37
57
|
# TODO: warn the primary key index is ignored
|
38
58
|
end
|
39
59
|
@indexes << Index.new(columns, opts)
|
@@ -57,24 +77,25 @@ module Mimi
|
|
57
77
|
}
|
58
78
|
end
|
59
79
|
|
80
|
+
# Represents a column in schema definition
|
81
|
+
#
|
60
82
|
class Column
|
61
|
-
|
62
|
-
as: :string,
|
63
|
-
limit: nil,
|
64
|
-
primary_key: false,
|
65
|
-
auto_increment: false,
|
66
|
-
not_null: false,
|
67
|
-
default: nil,
|
68
|
-
sql_type: nil
|
69
|
-
}.freeze
|
83
|
+
DEFAULT_TYPE = :string
|
70
84
|
|
71
|
-
attr_reader :name, :type, :params
|
85
|
+
attr_reader :name, :type, :sequel_type, :params
|
72
86
|
|
87
|
+
# Creates a Column object
|
88
|
+
#
|
89
|
+
# @param name [String,Symbol]
|
90
|
+
# @param opts [Hash]
|
91
|
+
#
|
73
92
|
def initialize(name, opts)
|
74
|
-
@name = name
|
75
|
-
@
|
76
|
-
|
77
|
-
@params =
|
93
|
+
@name = name.to_sym
|
94
|
+
@params = opts.dup
|
95
|
+
@params[:type] ||= @params[:as] || DEFAULT_TYPE
|
96
|
+
@params = Mimi::DB::Dictate::TypeDefaults.infer_params(@params)
|
97
|
+
@type = @params[:type]
|
98
|
+
@sequel_type = @params[:sequel_type]
|
78
99
|
end
|
79
100
|
|
80
101
|
def to_h
|
@@ -84,28 +105,35 @@ module Mimi
|
|
84
105
|
}
|
85
106
|
end
|
86
107
|
|
87
|
-
def
|
88
|
-
p = params.dup
|
89
|
-
p[:null] = !p[:not_null]
|
108
|
+
def to_sequel_params
|
109
|
+
p = params.dup.except(:type, :as)
|
110
|
+
p[:null] = !p[:not_null] if p.key?(:not_null)
|
90
111
|
p
|
91
112
|
end
|
92
113
|
|
114
|
+
def to_s
|
115
|
+
public_params = params.only(
|
116
|
+
:type, :primary_key, :auto_increment, :not_null, :default, :size
|
117
|
+
).select { |_, v| v }.to_h
|
118
|
+
public_params = public_params.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')
|
119
|
+
"#{name}(#{public_params})"
|
120
|
+
end
|
121
|
+
|
93
122
|
def ==(other)
|
94
123
|
unless other.name == name
|
95
124
|
raise ArgumentError, 'Cannot compare columns with different names'
|
96
125
|
end
|
97
126
|
equal = true
|
98
|
-
equal &&= params[:
|
99
|
-
equal &&= params[:limit] == other.params[:limit]
|
127
|
+
equal &&= params[:db_type] == other.params[:db_type]
|
100
128
|
equal &&= params[:primary_key] == other.params[:primary_key]
|
101
|
-
# FIXME: auto_increment ignored
|
102
129
|
equal &&= params[:not_null] == other.params[:not_null]
|
103
|
-
equal &&= params[:
|
104
|
-
# FIXME: sql_type ignored
|
130
|
+
equal &&= params[:db_default].to_s == other.params[:db_default].to_s
|
105
131
|
equal
|
106
132
|
end
|
107
133
|
end # class Column
|
108
134
|
|
135
|
+
# Represents an index in schema definition
|
136
|
+
#
|
109
137
|
class Index
|
110
138
|
DEFAULTS = {
|
111
139
|
unique: false
|
@@ -113,9 +141,14 @@ module Mimi
|
|
113
141
|
|
114
142
|
attr_reader :name, :columns, :params
|
115
143
|
|
144
|
+
# Creates an Index object
|
145
|
+
#
|
146
|
+
# @param columns [Array<String,Symbol>]
|
147
|
+
# @param params [Hash]
|
148
|
+
#
|
116
149
|
def initialize(columns, params)
|
117
150
|
@name = params[:name]
|
118
|
-
@columns = columns
|
151
|
+
@columns = columns.map(&:to_sym)
|
119
152
|
@params = DEFAULTS.merge(params)
|
120
153
|
end
|
121
154
|
|
@@ -5,11 +5,6 @@ module Mimi
|
|
5
5
|
module Dictate
|
6
6
|
module SchemaDiff
|
7
7
|
DEFAULT_OPTIONS = {
|
8
|
-
# Force updates on fields defined as :primary_key type.
|
9
|
-
# If disabled, the field definition will only be used in 'CREATE TABLE'.
|
10
|
-
# (forced updates on :primary_key break on Postgres, at least)
|
11
|
-
#
|
12
|
-
force_primary_key: false
|
13
8
|
}.freeze
|
14
9
|
|
15
10
|
#
|
@@ -18,7 +13,7 @@ module Mimi
|
|
18
13
|
# @return [Hash] :columns, :indexes => :from, :to
|
19
14
|
#
|
20
15
|
def self.diff(from, to, opts = {})
|
21
|
-
|
16
|
+
_options = DEFAULT_OPTIONS.merge(opts)
|
22
17
|
result = { table_name: from.table_name, columns: {}, indexes: {} }
|
23
18
|
all_column_names = (from.columns.values.map(&:name) + to.columns.values.map(&:name)).uniq
|
24
19
|
all_column_names.each do |c|
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mimi
|
4
|
+
module DB
|
5
|
+
module Dictate
|
6
|
+
module TypeDefaults
|
7
|
+
|
8
|
+
# Constructs a Proc that returns "<type>" or "<type>(n)" depending on whether the size is provided
|
9
|
+
#
|
10
|
+
DEF_OPT_SIZE_PROC = lambda do |type, default_size_suffix = ''|
|
11
|
+
->(p) { p[:size] ? { db_type: "#{type}(#{p[:size]})" } : { db_type: type + default_size_suffix } }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Produces a "string" or "string(n)" depending on whether the size is provided
|
15
|
+
#
|
16
|
+
TYPE_STRING_PROC = DEF_OPT_SIZE_PROC.call('string')
|
17
|
+
|
18
|
+
# Produces an "integer" or "integer(n)" depending on whether size is provided
|
19
|
+
#
|
20
|
+
TYPE_INTEGER_PROC = DEF_OPT_SIZE_PROC.call('integer')
|
21
|
+
|
22
|
+
# Produces a "decimal", "decimal(p)" or "decimal(p,s)" depending on whether size is provided
|
23
|
+
#
|
24
|
+
TYPE_DECIMAL_PROC = lambda do |p|
|
25
|
+
p[:size] ? { db_type: "decimal(#{[*p[:size]].join(',')})" } : { db_type: 'decimal' }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Produces a "decimal", "decimal(p)" or "decimal(p, s)" depending on whether size is provided
|
29
|
+
#
|
30
|
+
TYPE_SQLITE_DECIMAL_PROC = lambda do |p|
|
31
|
+
p[:size] ? { db_type: "decimal(#{[*p[:size]].join(', ')})" } : { db_type: 'decimal' }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Produces a "numeric" or "numeric(p, s)" depending on whether size is provided
|
35
|
+
#
|
36
|
+
TYPE_POSTGRES_NUMERIC_PROC = lambda do |p|
|
37
|
+
if p[:size]
|
38
|
+
# p[:size] is either a number (precision) or an array (precision, scale)
|
39
|
+
p1, p2 = [*p[:size]]
|
40
|
+
{ db_type: "numeric(#{p1},#{p2 || 0})" }
|
41
|
+
else
|
42
|
+
{ db_type: 'numeric' }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Produces a "character varying" or "character varying(n)" depending on whether the size is provided
|
47
|
+
#
|
48
|
+
TYPE_POSTGRES_VARCHAR_PROC = lambda do |p|
|
49
|
+
if p[:size]
|
50
|
+
{ db_type: "character varying(#{p[:size]})", sequel_type: :varchar }
|
51
|
+
else
|
52
|
+
{ db_type: 'character varying(255)', sequel_type: :varchar }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Type converters, accept Sequel type (field as: ...) as a key,
|
57
|
+
# return:
|
58
|
+
# * a Hash of field params or
|
59
|
+
# * a Proc returning a Hash of field params
|
60
|
+
#
|
61
|
+
TYPE_MAP = {
|
62
|
+
default: {
|
63
|
+
},
|
64
|
+
sqlite: {
|
65
|
+
bigint: { db_type: 'bigint' },
|
66
|
+
integer: TYPE_INTEGER_PROC,
|
67
|
+
int: { db_type: 'integer', sequel_type: :integer },
|
68
|
+
smallint: { db_type: 'smallint' },
|
69
|
+
|
70
|
+
decimal: TYPE_SQLITE_DECIMAL_PROC,
|
71
|
+
|
72
|
+
string: DEF_OPT_SIZE_PROC.call('string'),
|
73
|
+
varchar: DEF_OPT_SIZE_PROC.call('varchar', '(255)'),
|
74
|
+
text: { db_type: 'text' },
|
75
|
+
|
76
|
+
bytes: { db_type: 'bytea', sequel_type: :bytea },
|
77
|
+
bytea: { db_type: 'bytea' },
|
78
|
+
blob: { db_type: 'blob' },
|
79
|
+
|
80
|
+
bool: { db_type: 'bool', sequel_type: :bool },
|
81
|
+
boolean: { db_type: 'bool', sequel_type: :bool },
|
82
|
+
|
83
|
+
date: { db_type: 'date' },
|
84
|
+
timestamp: { db_type: 'timestamp' },
|
85
|
+
datetime: { db_type: 'timestamp', sequel_type: :timestamp },
|
86
|
+
|
87
|
+
float: { db_type: 'double precision' }
|
88
|
+
},
|
89
|
+
postgres: {
|
90
|
+
bigserial: { db_type: 'bigint', db_default: "nextval('tests_id_seq'::regclass)" },
|
91
|
+
bigint: { db_type: 'bigint' },
|
92
|
+
integer: { db_type: 'integer', sequel_type: :integer },
|
93
|
+
int: { db_type: 'integer', sequel_type: :integer },
|
94
|
+
smallint: { db_type: 'smallint' },
|
95
|
+
|
96
|
+
decimal: TYPE_POSTGRES_NUMERIC_PROC,
|
97
|
+
|
98
|
+
string: TYPE_POSTGRES_VARCHAR_PROC,
|
99
|
+
varchar: TYPE_POSTGRES_VARCHAR_PROC,
|
100
|
+
text: { db_type: 'text' },
|
101
|
+
|
102
|
+
bytes: { db_type: 'bytea', sequel_type: :bytea },
|
103
|
+
bytea: { db_type: 'bytea' },
|
104
|
+
blob: { db_type: 'bytea', sequel_type: :bytea },
|
105
|
+
|
106
|
+
bool: { db_type: 'boolean' },
|
107
|
+
boolean: { db_type: 'boolean' },
|
108
|
+
|
109
|
+
date: { db_type: 'date' },
|
110
|
+
timestamp: { db_type: 'timestamp without time zone' },
|
111
|
+
datetime: { db_type: 'timestamp without time zone', sequel_type: :timestamp },
|
112
|
+
|
113
|
+
float: { db_type: 'double precision' }
|
114
|
+
},
|
115
|
+
cockroachdb: {
|
116
|
+
bigserial: { db_type: 'bigint', db_default: 'unique_rowid()' },
|
117
|
+
bigint: { db_type: 'bigint' },
|
118
|
+
integer: { db_type: 'bigint' },
|
119
|
+
int: { db_type: 'bigint', sequel_type: :integer },
|
120
|
+
smallint: { db_type: 'bigint', sequel_type: :integer },
|
121
|
+
|
122
|
+
decimal: { db_type: 'numeric' },
|
123
|
+
|
124
|
+
string: { db_type: 'text', sequel_type: :string },
|
125
|
+
varchar: { db_type: 'text', sequel_type: :string },
|
126
|
+
text: { db_type: 'text' },
|
127
|
+
|
128
|
+
bytes: { db_type: 'bytea' },
|
129
|
+
bytea: { db_type: 'bytea', sequel_type: :bytes },
|
130
|
+
blob: { db_type: 'bytea', sequel_type: :bytes },
|
131
|
+
|
132
|
+
bool: { db_type: 'boolean' },
|
133
|
+
boolean: { db_type: 'boolean' },
|
134
|
+
|
135
|
+
date: { db_type: 'date' },
|
136
|
+
timestamp: { db_type: 'timestamp without time zone' },
|
137
|
+
datetime: { db_type: 'timestamp without time zone', sequel_type: :timestamp },
|
138
|
+
|
139
|
+
float: { db_type: 'double precision' }
|
140
|
+
}
|
141
|
+
}.freeze
|
142
|
+
|
143
|
+
#
|
144
|
+
# The method infers column params based on column definition
|
145
|
+
#
|
146
|
+
# @param params [Hash]
|
147
|
+
# @return [Hash]
|
148
|
+
#
|
149
|
+
def self.infer_params(params)
|
150
|
+
type = params[:type].to_sym
|
151
|
+
defaults = {
|
152
|
+
db_type: type.to_s,
|
153
|
+
sequel_type: type,
|
154
|
+
primary_key: !!params[:primary_key],
|
155
|
+
not_null: !!params[:primary_key],
|
156
|
+
db_default: params[:default]
|
157
|
+
}
|
158
|
+
adapter_name = Mimi::DB.sequel_config[:adapter]
|
159
|
+
raise "Failed to infer_params, adapter is not set: #{adapter_name}" unless adapter_name
|
160
|
+
inferred_params = TYPE_MAP[adapter_name.to_sym][type] || TYPE_MAP[:default][type] || {}
|
161
|
+
inferred_params = inferred_params.call(params) if inferred_params.is_a?(Proc)
|
162
|
+
defaults.merge(inferred_params).merge(params)
|
163
|
+
end
|
164
|
+
end # module TypeDefaults
|
165
|
+
end # module Dictate
|
166
|
+
end # module DB
|
167
|
+
end # module Mimi
|