mimi-db 0.2.7 → 0.3.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 +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
|