upsert 2.2.0 → 2.9.10
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 +5 -5
- data/.ruby-version +1 -0
- data/.standard.yml +1 -0
- data/.travis.yml +54 -31
- data/CHANGELOG +16 -0
- data/Gemfile +12 -1
- data/LICENSE +3 -1
- data/README.md +43 -8
- data/Rakefile +7 -1
- data/lib/upsert.rb +49 -7
- data/lib/upsert/column_definition/mysql.rb +2 -2
- data/lib/upsert/column_definition/postgresql.rb +9 -8
- data/lib/upsert/column_definition/sqlite3.rb +3 -3
- data/lib/upsert/connection/Java_ComMysqlJdbc_JDBC4Connection.rb +5 -3
- data/lib/upsert/connection/Java_OrgPostgresqlJdbc_PgConnection.rb +33 -0
- data/lib/upsert/connection/PG_Connection.rb +5 -0
- data/lib/upsert/connection/jdbc.rb +7 -1
- data/lib/upsert/connection/postgresql.rb +2 -3
- data/lib/upsert/merge_function.rb +3 -2
- data/lib/upsert/merge_function/{Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb → Java_OrgPostgresqlJdbc_PgConnection.rb} +2 -2
- data/lib/upsert/merge_function/PG_Connection.rb +2 -2
- data/lib/upsert/merge_function/postgresql.rb +81 -19
- data/lib/upsert/merge_function/sqlite3.rb +10 -0
- data/lib/upsert/version.rb +1 -1
- data/spec/correctness_spec.rb +20 -5
- data/spec/database_functions_spec.rb +6 -2
- data/spec/hstore_spec.rb +53 -38
- data/spec/logger_spec.rb +1 -1
- data/spec/postgresql_spec.rb +81 -3
- data/spec/reserved_words_spec.rb +18 -14
- data/spec/sequel_spec.rb +16 -7
- data/spec/spec_helper.rb +238 -111
- data/spec/speed_spec.rb +3 -33
- data/spec/threaded_spec.rb +35 -12
- data/spec/type_safety_spec.rb +2 -1
- data/travis/run_docker_db.sh +20 -0
- data/upsert-java.gemspec +13 -0
- data/upsert.gemspec +9 -58
- data/upsert.gemspec.common +107 -0
- metadata +39 -46
- data/lib/upsert/connection/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb +0 -20
data/spec/logger_spec.rb
CHANGED
data/spec/postgresql_spec.rb
CHANGED
@@ -1,16 +1,94 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'upsert/merge_function/postgresql'
|
3
|
+
|
2
4
|
describe Upsert do
|
3
|
-
version =
|
5
|
+
version = 'postgresql' == ENV['DB'] ? Upsert::MergeFunction::Postgresql.extract_version(
|
6
|
+
Pet.connection.select_value("SHOW server_version")
|
7
|
+
) : 0
|
4
8
|
|
5
9
|
let(:upsert) do
|
6
10
|
Upsert.new($conn, :pets)
|
7
11
|
end
|
8
12
|
|
9
|
-
it "uses the native method if available (#{(UNIQUE_CONSTRAINT && version >=
|
13
|
+
it "uses the native method if available (#{(UNIQUE_CONSTRAINT && version >= 90500).inspect})" do
|
10
14
|
p = Pet.create(:name => 'Jerry', :tag_number => 5)
|
11
15
|
upsert.row({ :name => 'Jerry'}, :tag_number => 6 )
|
12
16
|
expect(upsert.instance_variable_get(:@merge_function_cache).values.first.use_pg_native?).to(
|
13
|
-
UNIQUE_CONSTRAINT && version >=
|
17
|
+
UNIQUE_CONSTRAINT && version >= 90500 ? be_truthy : be_falsey
|
14
18
|
)
|
15
19
|
end
|
20
|
+
|
21
|
+
if version >= 90500 && UNIQUE_CONSTRAINT
|
22
|
+
it "works with a schema" do
|
23
|
+
table_name = ["#{RawConnectionFactory::DB_NAME}2", :pets2]
|
24
|
+
cls = clone_ar_class(Pet, table_name)
|
25
|
+
upsert = Upsert.new $conn, table_name
|
26
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'male'})
|
27
|
+
expect(upsert.instance_variable_get(:@merge_function_cache).values.first.use_pg_native?).to be_truthy
|
28
|
+
end
|
29
|
+
|
30
|
+
it "checks the correct table for a unique constraint" do
|
31
|
+
Pet.connection.execute("CREATE SCHEMA IF NOT EXISTS unique_constraint_test")
|
32
|
+
Pet.connection.execute("CREATE TABLE unique_constraint_test.pets (LIKE public.pets INCLUDING ALL)")
|
33
|
+
Pet.connection.execute("SET search_path TO unique_constraint_test")
|
34
|
+
|
35
|
+
if RUBY_PLATFORM == "java"
|
36
|
+
$conn.nativeSQL("SET search_path TO unique_constraint_test")
|
37
|
+
$conn.setSchema("unique_constraint_test")
|
38
|
+
else
|
39
|
+
$conn.exec("SET search_path TO unique_constraint_test")
|
40
|
+
end
|
41
|
+
|
42
|
+
Pet.connection.execute("DROP INDEX unique_constraint_test.pets_name_idx")
|
43
|
+
Pet.connection.execute("ALTER TABLE unique_constraint_test.pets DROP CONSTRAINT IF EXISTS unique_name")
|
44
|
+
p = Pet.create(:name => 'Jerry', :tag_number => 5)
|
45
|
+
upsert.row({ :name => 'Jerry'}, :tag_number => 6 )
|
46
|
+
expect(upsert.instance_variable_get(:@merge_function_cache).values.first.use_pg_native?).to be_falsey
|
47
|
+
Pet.connection.execute("SET search_path TO public")
|
48
|
+
|
49
|
+
if RUBY_PLATFORM == "java"
|
50
|
+
$conn.nativeSQL("SET search_path TO public")
|
51
|
+
$conn.setSchema("public")
|
52
|
+
else
|
53
|
+
$conn.exec("SET search_path TO public")
|
54
|
+
end
|
55
|
+
|
56
|
+
Pet.connection.execute("DROP SCHEMA unique_constraint_test CASCADE")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "array escaping" do
|
61
|
+
let(:upsert) do
|
62
|
+
Upsert.new($conn, :posts)
|
63
|
+
end
|
64
|
+
|
65
|
+
before(:all) do
|
66
|
+
Sequel.migration do
|
67
|
+
change do
|
68
|
+
db = self
|
69
|
+
create_table?(:posts) do
|
70
|
+
primary_key :id
|
71
|
+
String :name
|
72
|
+
column :tags, "text[]"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end.apply(DB, :up)
|
76
|
+
|
77
|
+
Object.const_set("Post", Class.new(ActiveRecord::Base))
|
78
|
+
end
|
79
|
+
|
80
|
+
[
|
81
|
+
%w[1 2 3],
|
82
|
+
%w[can't stop won't stop],
|
83
|
+
%w["''" '""' '\\],
|
84
|
+
["[]", "{}", "\\\\", "()"],
|
85
|
+
%w[*& *&^ $%IUBS (&^ ) ()*& // \\ \\\\ (*&^JN) (*HNCSD) ~!!!`` {} } { ( )],
|
86
|
+
%w[\\ \\\\ \\\\\\ \\\\\\\\ \\'\\'\'\\\'" \\'\\"\''\""],
|
87
|
+
].each do |arr|
|
88
|
+
it "properly upserts array of: #{arr}" do
|
89
|
+
upsert.row({name: "same-name"}, tags: arr)
|
90
|
+
expect(Post.first.tags).to eq(arr)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
16
94
|
end if ENV['DB'] == 'postgresql'
|
data/spec/reserved_words_spec.rb
CHANGED
@@ -11,29 +11,33 @@ describe Upsert do
|
|
11
11
|
# make lots of AR models, each of which has 10 columns named after these words
|
12
12
|
nasties = []
|
13
13
|
reserved_words.each_slice(10) do |words|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}
|
18
|
-
nasty = Object.const_get("Nasty#{nasties.length}")
|
14
|
+
name = "Nasty#{nasties.length}"
|
15
|
+
Object.const_set(name, Class.new(ActiveRecord::Base))
|
16
|
+
nasty = Object.const_get(name)
|
19
17
|
nasty.class_eval do
|
20
|
-
self.
|
21
|
-
|
22
|
-
words.each do |word|
|
23
|
-
col word, limit: 191
|
24
|
-
end
|
18
|
+
self.table_name = name.downcase
|
19
|
+
self.primary_key = "fake_primary_key"
|
25
20
|
end
|
21
|
+
|
22
|
+
Sequel.migration do
|
23
|
+
change do
|
24
|
+
db = self
|
25
|
+
create_table?(name.downcase) do
|
26
|
+
primary_key :fake_primary_key
|
27
|
+
words.each do |word|
|
28
|
+
String word, limit: 191
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end.apply(DB, :up)
|
26
33
|
nasties << [ nasty, words ]
|
27
34
|
end
|
28
|
-
nasties.each do |nasty, _|
|
29
|
-
nasty.auto_upgrade!
|
30
|
-
end
|
31
35
|
|
32
36
|
describe "reserved words" do
|
33
37
|
nasties.each do |nasty, words|
|
34
38
|
it "doesn't die on reserved words #{words.join(',')}" do
|
35
39
|
upsert = Upsert.new $conn, nasty.table_name
|
36
|
-
random = rand(1e3)
|
40
|
+
random = rand(1e3)
|
37
41
|
selector = { :fake_primary_key => random, words.first => words.first }
|
38
42
|
setter = words[1..-1].inject({}) { |memo, word| memo[word] = word; memo }
|
39
43
|
assert_creates nasty, [selector.merge(setter)] do
|
data/spec/sequel_spec.rb
CHANGED
@@ -6,17 +6,26 @@ describe Upsert do
|
|
6
6
|
config = ActiveRecord::Base.connection.instance_variable_get(:@config)
|
7
7
|
config[:adapter] = case config[:adapter]
|
8
8
|
when 'postgresql' then 'postgres'
|
9
|
-
when '
|
9
|
+
when 'sqlite3' then 'sqlite'
|
10
10
|
else config[:adapter]
|
11
11
|
end
|
12
12
|
|
13
13
|
let(:db) do
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
if RUBY_PLATFORM == "java"
|
15
|
+
Sequel.connect(
|
16
|
+
RawConnectionFactory::CONFIG,
|
17
|
+
:user => RawConnectionFactory::DB_USER,
|
18
|
+
:password => RawConnectionFactory::DB_PASSWORD
|
19
|
+
)
|
20
|
+
elsif config[:adapter] == "sqlite"
|
21
|
+
Sequel.sqlite("temp.db")
|
22
|
+
else
|
23
|
+
Sequel.connect(config.merge(
|
24
|
+
:user => config.values_at(:user, :username).compact.first,
|
25
|
+
:host => config.values_at(:host, :hostaddr).compact.first,
|
26
|
+
:database => config.values_at(:database, :dbname).compact.first
|
27
|
+
))
|
28
|
+
end
|
20
29
|
end
|
21
30
|
|
22
31
|
it "Doesn't explode on connection" do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,106 +1,182 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
require 'bundler/setup'
|
3
|
+
Bundler.require(:default, :development)
|
3
4
|
|
4
|
-
# require 'pry'
|
5
5
|
require 'shellwords'
|
6
|
+
require "sequel"
|
7
|
+
Sequel.default_timezone = :utc
|
8
|
+
Sequel.extension :migration
|
6
9
|
|
7
|
-
require
|
10
|
+
require "active_record"
|
11
|
+
require "activerecord-import"
|
8
12
|
ActiveRecord::Base.default_timezone = :utc
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
require 'activerecord-import' if RUBY_VERSION >= '1.9'
|
13
|
-
|
14
|
-
ENV['DB'] ||= 'mysql'
|
15
|
-
ENV['DB'] = 'postgresql' if ENV['DB'].to_s =~ /postgresql/
|
14
|
+
raise "A DB value is required" unless ENV["DB"]
|
15
|
+
ENV['DB'] = 'postgresql' if ENV['DB'].to_s =~ /postgresql/i
|
16
16
|
UNIQUE_CONSTRAINT = ENV['UNIQUE_CONSTRAINT'] == 'true'
|
17
17
|
|
18
|
+
raise "please use DB=postgresql NOT postgres" if ENV["DB"] == "postgres"
|
18
19
|
|
19
20
|
class RawConnectionFactory
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
password_argument = (PASSWORD.nil?) ? "" : "--password=#{Shellwords.escape(PASSWORD)}"
|
49
|
-
Kernel.system %{ mysql -h 127.0.0.1 -u #{CURRENT_USER} #{password_argument} -e "DROP DATABASE IF EXISTS #{DATABASE}" }
|
50
|
-
Kernel.system %{ mysql -h 127.0.0.1 -u #{CURRENT_USER} #{password_argument} -e "CREATE DATABASE #{DATABASE} CHARSET utf8mb4 COLLATE utf8mb4_general_ci" }
|
51
|
-
if RUBY_PLATFORM == 'java'
|
52
|
-
CONFIG = "jdbc:mysql://127.0.0.1/#{DATABASE}?user=#{CURRENT_USER}&password=#{PASSWORD}"
|
53
|
-
require 'jdbc/mysql'
|
54
|
-
Jdbc::MySQL.load_driver
|
55
|
-
# java.sql.DriverManager.register_driver com.mysql.jdbc.Driver.new
|
56
|
-
def new_connection
|
57
|
-
java.sql.DriverManager.get_connection CONFIG
|
58
|
-
end
|
59
|
-
else
|
60
|
-
require 'mysql2'
|
61
|
-
def new_connection
|
62
|
-
config = { :username => CURRENT_USER, :database => DATABASE, :host => "127.0.0.1", :encoding => 'utf8mb4' }
|
63
|
-
config.merge!(:password => PASSWORD) unless PASSWORD.nil?
|
64
|
-
Mysql2::Client.new config
|
65
|
-
end
|
66
|
-
end
|
67
|
-
ActiveRecord::Base.establish_connection(
|
68
|
-
:adapter => RUBY_PLATFORM == 'java' ? 'mysql' : 'mysql2',
|
69
|
-
:user => CURRENT_USER,
|
70
|
-
:password => PASSWORD,
|
71
|
-
:host => '127.0.0.1',
|
72
|
-
:database => DATABASE,
|
73
|
-
:encoding => 'utf8mb4'
|
21
|
+
DB_NAME = ENV['DB_NAME'] || 'upsert_test'
|
22
|
+
# You *need* to specific DB_USER on certain combinations of JRuby/JDK as spawning a shell
|
23
|
+
# has some oddities
|
24
|
+
DB_USER = (ENV['DB_USER'] || `whoami`.chomp).to_s
|
25
|
+
raise "A DB_USER value is required" if DB_USER.empty?
|
26
|
+
DB_PASSWORD = ENV['DB_PASSWORD']
|
27
|
+
DB_HOST = ENV['DB_HOST'] || '127.0.0.1'
|
28
|
+
|
29
|
+
def self.db_env
|
30
|
+
@db_env ||= base_params(nil, false).map { |k, v| [":#{k}", v.to_s.empty? ? nil : Shellwords.escape(v)] }.to_h
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.adapter_name(adapter = nil)
|
34
|
+
RUBY_PLATFORM != "java" && adapter == "mysql" ? "mysql2" : adapter
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.base_params(adapter = nil, show_additional_params = true)
|
38
|
+
return { :adapter => "sqlite3", :database => "temp.db", cache: "shared" } if adapter == "sqlite3"
|
39
|
+
{
|
40
|
+
host: DB_HOST,
|
41
|
+
database: DB_NAME,
|
42
|
+
dbname: DB_NAME,
|
43
|
+
username: DB_USER,
|
44
|
+
user: DB_USER,
|
45
|
+
password: DB_PASSWORD,
|
46
|
+
adapter: adapter,
|
47
|
+
}.merge(
|
48
|
+
show_additional_params ? additional_params(adapter) : {}
|
74
49
|
)
|
75
|
-
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.additional_params(adapter = nil)
|
53
|
+
{
|
54
|
+
"mysql" => { encoding: "utf8mb4" },
|
55
|
+
"mysql2" => { encoding: "utf8mb4" },
|
56
|
+
}.fetch(adapter, {})
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.postgresql_call(string)
|
60
|
+
Kernel.system "PGHOST=#{db_env[":host"]} PGUSER=#{db_env[":user"]} PGPASSWORD=#{db_env[":password"]} #{string.gsub(/:[a-z]+/, db_env)}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.mysql_call(string)
|
64
|
+
Kernel.system "mysql -h #{db_env[":host"]} -u #{db_env[":user"]} --password=#{db_env[":password"]} #{string.gsub(/:[a-z]+/, db_env)}"
|
65
|
+
end
|
76
66
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
67
|
+
SYSTEM_CALLS = {
|
68
|
+
"postgresql" => [
|
69
|
+
%{ dropdb :dbname },
|
70
|
+
%{ createdb :dbname },
|
71
|
+
%{ psql -d :dbname -c 'DROP SCHEMA IF EXISTS :dbname2 CASCASE' },
|
72
|
+
%{ psql -d :dbname -c 'CREATE SCHEMA :dbname2' },
|
73
|
+
],
|
74
|
+
"mysql" => [
|
75
|
+
%{ -e "DROP DATABASE IF EXISTS :dbname" },
|
76
|
+
%{ -e "DROP DATABASE IF EXISTS :dbname2" },
|
77
|
+
%{ -e "CREATE DATABASE :dbname CHARSET utf8mb4 COLLATE utf8mb4_general_ci" },
|
78
|
+
%{ -e "CREATE DATABASE :dbname2 CHARSET utf8mb4 COLLATE utf8mb4_general_ci" },
|
79
|
+
]
|
80
|
+
}.freeze
|
81
|
+
|
82
|
+
REQUIRES = {
|
83
|
+
"mysql" => "mysql2",
|
84
|
+
"postgresql" => "pg",
|
85
|
+
"sqlite3" => "sqlite3",
|
86
|
+
"java-postgresql" => "jdbc/postgres",
|
87
|
+
"java-mysql" => "jdbc/mysql",
|
88
|
+
"java-sqlite3" => "jdbc/sqlite3",
|
89
|
+
}.freeze
|
90
|
+
|
91
|
+
NEW_CONNECTION = {
|
92
|
+
"postgresql" => ->(base_params) { PG::Connection.new(base_params.except(:database, :username, :adapter)) },
|
93
|
+
"mysql" => ->(base_params) { Mysql2::Client.new(base_params) },
|
94
|
+
"sqlite3" => ->(base_params) { ActiveRecord::Base.connection.raw_connection },
|
95
|
+
}
|
96
|
+
|
97
|
+
POST_CONNECTION = {
|
98
|
+
"mysql" => -> { ActiveRecord::Base.connection.execute "SET NAMES utf8mb4 COLLATE utf8mb4_general_ci" },
|
99
|
+
"sqlite3" => -> { [ActiveRecord::Base.connection, ::DB].each { |c| c.execute "ATTACH DATABASE 'temp2.db' AS #{DB_NAME}2" } },
|
100
|
+
}
|
101
|
+
|
102
|
+
SYSTEM_CALLS.fetch(ENV["DB"], []).each do |str|
|
103
|
+
send("#{ENV["DB"]}_call", str)
|
104
|
+
end
|
105
|
+
|
106
|
+
if RUBY_PLATFORM == 'java'
|
107
|
+
CONFIG = "jdbc:#{ENV["DB"]}://#{DB_HOST}/#{DB_NAME}"
|
108
|
+
require REQUIRES["java-#{ENV["DB"]}"]
|
109
|
+
|
110
|
+
case ENV["DB"]
|
111
|
+
when "postgresql" then Jdbc::Postgres.load_driver
|
112
|
+
when "mysql" then Jdbc::MySQL.load_driver
|
113
|
+
when "sqlite3"
|
82
114
|
Jdbc::SQLite3.load_driver
|
115
|
+
CONFIG = "jdbc:sqlite::memory:?cache=shared"
|
116
|
+
end
|
117
|
+
|
118
|
+
def new_connection
|
119
|
+
java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("+00:00"))
|
120
|
+
java.sql.DriverManager.get_connection CONFIG, DB_USER, DB_PASSWORD
|
121
|
+
end
|
122
|
+
else
|
123
|
+
case ENV['DB']
|
124
|
+
when "postgresql", "mysql"
|
125
|
+
require REQUIRES[ENV["DB"]]
|
83
126
|
def new_connection
|
84
|
-
|
127
|
+
NEW_CONNECTION[ENV["DB"]].call(self.class.base_params(ENV["DB"]))
|
85
128
|
end
|
86
|
-
|
87
|
-
require
|
129
|
+
when "sqlite3"
|
130
|
+
require REQUIRES[ENV["DB"]]
|
88
131
|
def new_connection
|
89
|
-
|
132
|
+
NEW_CONNECTION[ENV["DB"]].call(self.class.base_params(ENV["DB"]))
|
90
133
|
end
|
134
|
+
CONFIG = { :adapter => "sqlite3", :database => "temp.db", cache: "shared" }
|
135
|
+
|
91
136
|
end
|
92
|
-
|
137
|
+
end
|
93
138
|
|
94
|
-
|
95
|
-
|
139
|
+
ActiveRecord::Base.establish_connection(
|
140
|
+
base_params(adapter_name(ENV["DB"]))
|
141
|
+
)
|
142
|
+
ari_adapter_name = adapter_name(ENV["DB"]) == "mysql" ? "mysql2" : adapter_name(ENV["DB"])
|
143
|
+
require "activerecord-import/active_record/adapters/#{ari_adapter_name}_adapter"
|
144
|
+
end
|
96
145
|
|
97
|
-
|
98
|
-
|
99
|
-
|
146
|
+
raise "not supported" unless RawConnectionFactory.instance_methods.include?(:new_connection)
|
147
|
+
|
148
|
+
config = ActiveRecord::Base.connection.instance_variable_get(:@config)
|
149
|
+
config[:adapter] = case config[:adapter]
|
150
|
+
when "postgresql" then "postgres"
|
151
|
+
when "sqlite3" then "sqlite"
|
152
|
+
else config[:adapter]
|
153
|
+
end
|
154
|
+
params = if RUBY_PLATFORM == "java"
|
155
|
+
RawConnectionFactory::CONFIG
|
156
|
+
else
|
157
|
+
config.merge(
|
158
|
+
:user => config.values_at(:user, :username).compact.first,
|
159
|
+
:host => config.values_at(:host, :hostaddr).compact.first,
|
160
|
+
:database => config.values_at(:database, :dbname).compact.first
|
161
|
+
)
|
162
|
+
end
|
163
|
+
DB = if RUBY_PLATFORM == "java"
|
164
|
+
Sequel.connect(
|
165
|
+
params,
|
166
|
+
:user => RawConnectionFactory::DB_USER,
|
167
|
+
:password => RawConnectionFactory::DB_PASSWORD
|
168
|
+
)
|
169
|
+
elsif ENV["DB"] == "sqlite3"
|
170
|
+
Kernel.at_exit { FileUtils.rm(Dir.glob("temp*.db")) }
|
171
|
+
Sequel.sqlite("temp.db")
|
172
|
+
else
|
173
|
+
Sequel.connect(params)
|
100
174
|
end
|
101
175
|
|
102
176
|
$conn_factory = RawConnectionFactory.new
|
103
177
|
$conn = $conn_factory.new_connection
|
178
|
+
RawConnectionFactory::POST_CONNECTION.fetch(ENV["DB"], -> {}).call
|
179
|
+
|
104
180
|
|
105
181
|
require 'logger'
|
106
182
|
require 'fileutils'
|
@@ -113,49 +189,71 @@ else
|
|
113
189
|
ActiveRecord::Base.logger.level = Logger::WARN
|
114
190
|
end
|
115
191
|
|
116
|
-
|
117
|
-
col :name, limit: 191 # utf8mb4 in mysql requirement
|
118
|
-
col :gender
|
119
|
-
col :spiel
|
120
|
-
col :good, :type => :boolean
|
121
|
-
col :lovability, :type => :float
|
122
|
-
col :morning_walk_time, :type => :datetime
|
123
|
-
col :zipped_biography, :type => :binary
|
124
|
-
col :tag_number, :type => :integer
|
125
|
-
col :big_tag_number, :type => :bigint
|
126
|
-
col :birthday, :type => :date
|
127
|
-
col :home_address, :type => :text
|
128
|
-
if ENV['DB'] == 'postgresql'
|
129
|
-
col :tsntz, :type => 'timestamp without time zone'
|
130
|
-
end
|
131
|
-
add_index :name, :unique => true
|
132
|
-
end
|
133
|
-
if ENV['DB'] == 'postgresql' && UNIQUE_CONSTRAINT
|
192
|
+
if ENV['DB'] == 'postgresql'
|
134
193
|
begin
|
135
|
-
|
194
|
+
DB << "ALTER TABLE pets DROP CONSTRAINT IF EXISTS unique_name"
|
136
195
|
rescue => e
|
137
196
|
puts e.inspect
|
138
197
|
end
|
139
198
|
end
|
140
199
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
200
|
+
class InternalMigration
|
201
|
+
DEFINITIONS = {
|
202
|
+
pets: ->(db) {
|
203
|
+
primary_key :id
|
204
|
+
String :name, { size: 191 }.merge(ENV["DB"] == "mysql" || UNIQUE_CONSTRAINT ? { index: { unique: true } } : {})
|
205
|
+
String :gender
|
206
|
+
String :spiel
|
207
|
+
TrueClass :good
|
208
|
+
BigDecimal :lovability, size: [30, 15] # 15 integer digits and 15 fractional digits
|
209
|
+
DateTime :morning_walk_time
|
210
|
+
File :zipped_biography
|
211
|
+
Integer :tag_number
|
212
|
+
Bignum :big_tag_number
|
213
|
+
Date :birthday
|
214
|
+
String :home_address, text: true
|
215
|
+
|
216
|
+
if db.database_type == :postgres
|
217
|
+
column :tsntz, "timestamp without time zone"
|
218
|
+
end
|
219
|
+
},
|
220
|
+
tasks: ->(db) {
|
221
|
+
primary_key :id
|
222
|
+
String :name
|
223
|
+
DateTime :created_at
|
224
|
+
DateTime :created_on
|
225
|
+
},
|
226
|
+
people: ->(db) {
|
227
|
+
primary_key :id
|
228
|
+
String :"First Name"
|
229
|
+
String :"Last Name"
|
230
|
+
},
|
231
|
+
alphabets: ->(db) {
|
232
|
+
("a".."z").each do |col|
|
233
|
+
Integer "the_letter_#{col}".to_sym
|
234
|
+
end
|
235
|
+
}
|
236
|
+
}
|
145
237
|
end
|
146
238
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
239
|
+
Sequel.migration do
|
240
|
+
change do
|
241
|
+
db = self
|
242
|
+
InternalMigration::DEFINITIONS.each do |table, blk|
|
243
|
+
create_table?(table) do
|
244
|
+
instance_exec(db, &blk)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end.apply(DB, :up)
|
249
|
+
|
250
|
+
if ENV['DB'] == 'postgresql' && UNIQUE_CONSTRAINT
|
251
|
+
DB << "ALTER TABLE pets ADD CONSTRAINT unique_name UNIQUE (name)"
|
151
252
|
end
|
152
|
-
Task.auto_upgrade!
|
153
253
|
|
154
|
-
|
155
|
-
|
156
|
-
col :"Last Name"
|
254
|
+
%i[Pet Task Person Alphabet].each do |name|
|
255
|
+
Object.const_set(name, Class.new(ActiveRecord::Base))
|
157
256
|
end
|
158
|
-
Person.auto_upgrade!
|
159
257
|
|
160
258
|
require 'zlib'
|
161
259
|
require 'benchmark'
|
@@ -188,7 +286,7 @@ module SpecHelper
|
|
188
286
|
names.choice
|
189
287
|
end
|
190
288
|
setter = {
|
191
|
-
:lovability => BigDecimal
|
289
|
+
:lovability => BigDecimal(rand(1e11).to_s, 2),
|
192
290
|
:tag_number => rand(1e8),
|
193
291
|
:spiel => Faker::Lorem.sentences.join,
|
194
292
|
:good => true,
|
@@ -278,6 +376,35 @@ module SpecHelper
|
|
278
376
|
upsert_time.should be < ar_time
|
279
377
|
$stderr.puts " Upsert was #{((ar_time - upsert_time) / ar_time * 100).round}% faster than #{competition}"
|
280
378
|
end
|
379
|
+
|
380
|
+
def clone_ar_class(klass, table_name)
|
381
|
+
u = Upsert.new $conn, klass.table_name
|
382
|
+
new_table_name = [*table_name].compact
|
383
|
+
# AR's support for quoting of schema and table names is horrendous
|
384
|
+
# schema.table and schema.`table` are considiered different names on MySQL, but
|
385
|
+
# schema.table and schema."table" are correctly considered the same on Postgres
|
386
|
+
sequel_table_name = new_table_name.map(&:to_sym)
|
387
|
+
new_table_name[-1] = u.connection.quote_ident(new_table_name[-1]) if new_table_name[-1].to_s.index('.')
|
388
|
+
new_table_name = new_table_name.join('.')
|
389
|
+
|
390
|
+
Sequel.migration do
|
391
|
+
change do
|
392
|
+
db = self
|
393
|
+
create_table?(sequel_table_name.length > 1 ? Sequel.qualify(*sequel_table_name) : sequel_table_name.first) do
|
394
|
+
instance_exec(db, &InternalMigration::DEFINITIONS[klass.table_name.to_sym])
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end.apply(DB, :up)
|
398
|
+
|
399
|
+
cls = Class.new(klass)
|
400
|
+
cls.class_eval do
|
401
|
+
self.table_name = new_table_name
|
402
|
+
def self.quoted_table_name
|
403
|
+
new_table_name
|
404
|
+
end
|
405
|
+
end
|
406
|
+
cls
|
407
|
+
end
|
281
408
|
end
|
282
409
|
|
283
410
|
RSpec.configure do |c|
|