baza 0.0.20 → 0.0.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +84 -0
  3. data/.rubocop_todo.yml +17 -135
  4. data/.travis.yml +21 -0
  5. data/Gemfile +10 -7
  6. data/Gemfile.lock +39 -44
  7. data/README.md +61 -3
  8. data/VERSION +1 -1
  9. data/baza.gemspec +146 -98
  10. data/config/best_project_practice_rubocop.yml +8 -0
  11. data/config/best_project_practice_rubocop_todo.yml +6 -0
  12. data/lib/baza.rb +8 -12
  13. data/lib/baza/base_sql_driver.rb +198 -52
  14. data/lib/baza/cloner.rb +1 -0
  15. data/lib/baza/column.rb +26 -0
  16. data/lib/baza/database.rb +19 -3
  17. data/lib/baza/db.rb +69 -271
  18. data/lib/baza/driver.rb +1 -6
  19. data/lib/baza/{drivers → driver}/active_record.rb +65 -21
  20. data/lib/baza/{drivers → driver}/active_record/columns.rb +0 -0
  21. data/lib/baza/driver/active_record/commands.rb +10 -0
  22. data/lib/baza/driver/active_record/databases.rb +10 -0
  23. data/lib/baza/{drivers → driver}/active_record/indexes.rb +0 -0
  24. data/lib/baza/{drivers → driver}/active_record/result.rb +3 -1
  25. data/lib/baza/{drivers → driver}/active_record/tables.rb +0 -0
  26. data/lib/baza/driver/active_record/users.rb +12 -0
  27. data/lib/baza/{drivers → driver}/mysql.rb +9 -26
  28. data/lib/baza/{drivers → driver}/mysql/column.rb +14 -35
  29. data/lib/baza/{drivers → driver}/mysql/columns.rb +9 -12
  30. data/lib/baza/driver/mysql/commands.rb +39 -0
  31. data/lib/baza/driver/mysql/database.rb +64 -0
  32. data/lib/baza/driver/mysql/databases.rb +63 -0
  33. data/lib/baza/{drivers → driver}/mysql/index.rb +0 -0
  34. data/lib/baza/{drivers → driver}/mysql/indexes.rb +0 -0
  35. data/lib/baza/{drivers → driver}/mysql/result.rb +15 -7
  36. data/lib/baza/{drivers → driver}/mysql/sqlspecs.rb +0 -0
  37. data/lib/baza/{drivers → driver}/mysql/table.rb +27 -43
  38. data/lib/baza/{drivers → driver}/mysql/tables.rb +5 -34
  39. data/lib/baza/{drivers → driver}/mysql/unbuffered_result.rb +8 -2
  40. data/lib/baza/driver/mysql/user.rb +22 -0
  41. data/lib/baza/driver/mysql/users.rb +39 -0
  42. data/lib/baza/{drivers → driver}/mysql2.rb +19 -49
  43. data/lib/baza/{drivers → driver}/mysql2/column.rb +0 -0
  44. data/lib/baza/{drivers → driver}/mysql2/columns.rb +0 -0
  45. data/lib/baza/driver/mysql2/commands.rb +2 -0
  46. data/lib/baza/{drivers → driver}/mysql2/database.rb +0 -0
  47. data/lib/baza/{drivers → driver}/mysql2/databases.rb +0 -0
  48. data/lib/baza/{drivers → driver}/mysql2/index.rb +0 -0
  49. data/lib/baza/{drivers → driver}/mysql2/indexes.rb +0 -0
  50. data/lib/baza/{drivers → driver}/mysql2/result.rb +3 -1
  51. data/lib/baza/{drivers → driver}/mysql2/table.rb +0 -0
  52. data/lib/baza/{drivers → driver}/mysql2/tables.rb +0 -0
  53. data/lib/baza/driver/mysql2/user.rb +2 -0
  54. data/lib/baza/driver/mysql2/users.rb +2 -0
  55. data/lib/baza/{drivers → driver}/mysql_java.rb +60 -38
  56. data/lib/baza/{drivers → driver}/mysql_java/column.rb +0 -0
  57. data/lib/baza/{drivers → driver}/mysql_java/columns.rb +0 -0
  58. data/lib/baza/driver/mysql_java/commands.rb +2 -0
  59. data/lib/baza/driver/mysql_java/database.rb +2 -0
  60. data/lib/baza/driver/mysql_java/databases.rb +2 -0
  61. data/lib/baza/{drivers → driver}/mysql_java/index.rb +0 -0
  62. data/lib/baza/{drivers → driver}/mysql_java/indexes.rb +0 -0
  63. data/lib/baza/{drivers → driver}/mysql_java/table.rb +0 -0
  64. data/lib/baza/{drivers → driver}/mysql_java/tables.rb +0 -0
  65. data/lib/baza/driver/mysql_java/user.rb +2 -0
  66. data/lib/baza/driver/mysql_java/users.rb +2 -0
  67. data/lib/baza/driver/pg.rb +80 -0
  68. data/lib/baza/driver/pg/column.rb +125 -0
  69. data/lib/baza/driver/pg/columns.rb +37 -0
  70. data/lib/baza/driver/pg/commands.rb +35 -0
  71. data/lib/baza/driver/pg/create_index_sql_creator.rb +51 -0
  72. data/lib/baza/driver/pg/database.rb +89 -0
  73. data/lib/baza/driver/pg/databases.rb +79 -0
  74. data/lib/baza/driver/pg/index.rb +35 -0
  75. data/lib/baza/driver/pg/indexes.rb +5 -0
  76. data/lib/baza/driver/pg/result.rb +139 -0
  77. data/lib/baza/driver/pg/table.rb +184 -0
  78. data/lib/baza/driver/pg/tables.rb +45 -0
  79. data/lib/baza/{drivers → driver}/sqlite3.rb +6 -24
  80. data/lib/baza/{drivers → driver}/sqlite3/column.rb +22 -24
  81. data/lib/baza/{drivers → driver}/sqlite3/columns.rb +6 -6
  82. data/lib/baza/driver/sqlite3/commands.rb +28 -0
  83. data/lib/baza/{drivers → driver}/sqlite3/database.rb +0 -0
  84. data/lib/baza/{drivers → driver}/sqlite3/databases.rb +0 -1
  85. data/lib/baza/{drivers → driver}/sqlite3/index.rb +0 -0
  86. data/lib/baza/{drivers → driver}/sqlite3/indexes.rb +0 -0
  87. data/lib/baza/{drivers → driver}/sqlite3/result.rb +14 -6
  88. data/lib/baza/{drivers → driver}/sqlite3/sqlspecs.rb +0 -0
  89. data/lib/baza/{drivers → driver}/sqlite3/table.rb +25 -16
  90. data/lib/baza/{drivers → driver}/sqlite3/tables.rb +5 -6
  91. data/lib/baza/{drivers → driver}/sqlite3/unbuffered_result.rb +8 -2
  92. data/lib/baza/{drivers → driver}/sqlite3_java.rb +13 -23
  93. data/lib/baza/{drivers → driver}/sqlite3_java/column.rb +0 -0
  94. data/lib/baza/{drivers → driver}/sqlite3_java/columns.rb +0 -0
  95. data/lib/baza/driver/sqlite3_java/commands.rb +2 -0
  96. data/lib/baza/{drivers → driver}/sqlite3_java/database.rb +0 -0
  97. data/lib/baza/{drivers → driver}/sqlite3_java/index.rb +0 -0
  98. data/lib/baza/{drivers → driver}/sqlite3_java/indexes.rb +0 -0
  99. data/lib/baza/{drivers → driver}/sqlite3_java/table.rb +0 -0
  100. data/lib/baza/{drivers → driver}/sqlite3_java/tables.rb +0 -0
  101. data/lib/baza/{drivers → driver}/sqlite3_java/unbuffered_result.rb +14 -9
  102. data/lib/baza/{drivers → driver}/sqlite3_rhodes.rb +6 -24
  103. data/lib/baza/errors.rb +2 -0
  104. data/lib/baza/idquery.rb +15 -8
  105. data/lib/baza/index.rb +7 -0
  106. data/lib/baza/jdbc_driver.rb +4 -16
  107. data/lib/baza/jdbc_result.rb +20 -12
  108. data/lib/baza/mysql_base_driver.rb +7 -7
  109. data/lib/baza/query_buffer.rb +20 -19
  110. data/lib/baza/row.rb +16 -16
  111. data/lib/baza/sql_queries.rb +3 -0
  112. data/lib/baza/sql_queries/generic_insert.rb +81 -0
  113. data/lib/baza/sql_queries/generic_update.rb +31 -0
  114. data/lib/baza/sql_queries/mysql_upsert.rb +52 -0
  115. data/lib/baza/sql_queries/mysql_upsert_duplicate_key.rb +57 -0
  116. data/lib/baza/sql_queries/non_atomic_upsert.rb +25 -0
  117. data/lib/baza/sql_queries/postgres_upsert_duplicate_key.rb +118 -0
  118. data/lib/baza/sql_queries/select.rb +170 -0
  119. data/lib/baza/sql_queries/sqlite_upsert_duplicate_key.rb +99 -0
  120. data/lib/baza/table.rb +35 -8
  121. data/spec/active_record/models/user.rb +3 -0
  122. data/spec/{cloner_spec.rb → baza/cloner_spec.rb} +0 -0
  123. data/spec/drivers/active_record_mysql2_spec.rb +5 -3
  124. data/spec/drivers/active_record_mysql_spec.rb +2 -1
  125. data/spec/drivers/active_record_pg_spec.rb +20 -0
  126. data/spec/drivers/active_record_sqlite3_spec.rb +2 -1
  127. data/spec/drivers/mysql2_spec.rb +1 -1
  128. data/spec/drivers/mysql_spec.rb +10 -10
  129. data/spec/drivers/pg_spec.rb +18 -0
  130. data/spec/drivers/sqlite3_spec.rb +7 -8
  131. data/spec/info_active_record_example.rb +1 -1
  132. data/spec/{info_active_record_mysql2.rb → info_active_record_mysql2_example.rb} +3 -2
  133. data/spec/info_active_record_mysql2_travis.rb +35 -0
  134. data/spec/{info_active_record_mysql.rb → info_active_record_mysql_example.rb} +5 -4
  135. data/spec/info_active_record_mysql_travis.rb +36 -0
  136. data/spec/info_active_record_pg_example.rb +36 -0
  137. data/spec/info_active_record_pg_travis.rb +34 -0
  138. data/spec/info_active_record_sqlite3.rb +1 -1
  139. data/spec/info_mysql2_example.rb +1 -3
  140. data/spec/{info_mysql2_shippable.rb → info_mysql2_travis.rb} +2 -4
  141. data/spec/info_mysql_example.rb +1 -3
  142. data/spec/{info_mysql_shippable.rb → info_mysql_travis.rb} +2 -4
  143. data/spec/info_pg_example.rb +22 -0
  144. data/spec/info_pg_travis.rb +20 -0
  145. data/spec/info_sqlite3.rb +1 -3
  146. data/spec/spec_helper.rb +1 -1
  147. data/spec/support/driver_active_record_collection.rb +62 -0
  148. data/spec/support/driver_collection.rb +136 -121
  149. data/spec/support/driver_columns_collection.rb +19 -10
  150. data/spec/support/driver_databases_collection.rb +23 -1
  151. data/spec/support/driver_indexes_collection.rb +2 -2
  152. data/spec/support/driver_tables_collection.rb +24 -4
  153. data/spec/support/driver_users_collection.rb +53 -0
  154. metadata +185 -104
  155. data/lib/baza/drivers/mysql/database.rb +0 -28
  156. data/lib/baza/drivers/mysql/databases.rb +0 -35
  157. data/lib/baza/drivers/mysql_java/database.rb +0 -2
  158. data/lib/baza/drivers/mysql_java/databases.rb +0 -2
  159. data/lib/baza/model.rb +0 -875
  160. data/lib/baza/model_custom.rb +0 -155
  161. data/lib/baza/model_handler.rb +0 -910
  162. data/lib/baza/model_handler_sqlhelper.rb +0 -484
  163. data/lib/baza/revision.rb +0 -383
  164. data/shippable.yml +0 -17
  165. data/spec/info_active_record_mysql2_shippable.rb +0 -34
  166. data/spec/info_active_record_mysql_shippable.rb +0 -34
  167. data/spec/model_handler_spec.rb +0 -431
@@ -13,24 +13,24 @@ class Baza::Row
13
13
  @args[key.to_sym] = value
14
14
  end
15
15
 
16
- @args[:objects] = $objects if !@args[:objects] && $objects && $objects.is_a?(Baza::ModelHandler)
17
16
  @args[:col_id] ||= :id
18
17
  raise "No table given." unless @args[:table]
19
18
 
20
19
  if @args[:data] && (@args[:data].is_a?(Integer) || @args[:data].is_a?(Fixnum) || @args[:data].is_a?(String))
21
20
  @data = {@args[:col_id].to_sym => @args[:data].to_s}
22
21
  reload
23
- elsif @args[:data] && @args[:data].is_a?(Hash)
22
+ elsif @args[:data] && @args.fetch(:data).is_a?(Hash)
24
23
  @data = {}
25
- @args[:data].each do |key, value|
26
- @data[key.to_sym] = value
24
+ @args.fetch(:data).each do |key, value|
25
+ key = key.to_sym unless key.is_a?(Fixnum)
26
+ @data[key] = value
27
27
  end
28
28
  elsif @args[:id]
29
29
  @data = {}
30
30
  @data[@args[:col_id].to_sym] = @args[:id]
31
31
  reload
32
32
  else
33
- raise ArgumentError.new("Invalid data: #{@args[:data]} (#{@args[:data].class})")
33
+ raise ArgumentError, "Invalid data: #{@args[:data]} (#{@args[:data].class})"
34
34
  end
35
35
  end
36
36
 
@@ -43,7 +43,7 @@ class Baza::Row
43
43
  false
44
44
  end
45
45
 
46
- alias_method :objects, :ob
46
+ alias objects ob
47
47
 
48
48
  def reload
49
49
  last_id = id
@@ -59,14 +59,14 @@ class Baza::Row
59
59
  end
60
60
 
61
61
  def update(newdata)
62
- db.update(@args[:table], newdata, @args[:col_id] => id)
62
+ db.update(@args.fetch(:table), newdata, @args.fetch(:col_id) => id)
63
63
  reload
64
64
 
65
65
  ob.call("object" => self, "signal" => "update") if ob
66
66
  end
67
67
 
68
68
  def delete
69
- db.delete(@args[:table], @args[:col_id] => id)
69
+ db.delete(@args.fetch(:table), @args.fetch(:col_id) => id)
70
70
  destroy
71
71
  end
72
72
 
@@ -100,7 +100,7 @@ class Baza::Row
100
100
  end
101
101
 
102
102
  def id
103
- @data[@args[:col_id]]
103
+ @data.fetch(@args.fetch(:col_id))
104
104
  end
105
105
 
106
106
  def to_param
@@ -108,18 +108,18 @@ class Baza::Row
108
108
  end
109
109
 
110
110
  def title
111
- return @data[@args[:col_title].to_sym] if @args[:col_title]
111
+ return @data[@args.fetch(:col_title).to_sym] if @args[:col_title]
112
112
 
113
113
  if @data.key?(:title)
114
- return @data[:title]
114
+ return @data.fetch(:title)
115
115
  elsif @data.key?(:name)
116
- return @data[:name]
116
+ return @data.fetch(:name)
117
117
  end
118
118
 
119
119
  raise "'col_title' has not been set for the class: '#{self.class}'."
120
120
  end
121
121
 
122
- alias_method :name, :title
122
+ alias name title
123
123
 
124
124
  def each(*args, &blk)
125
125
  @data.each(*args, &blk)
@@ -138,10 +138,10 @@ class Baza::Row
138
138
  end
139
139
 
140
140
  def method_missing(func_name, *args)
141
- if match = func_name.to_s.match(/^(\S+)\?$/) && @data.key?(match[1].to_sym)
142
- if @data[match[1].to_sym] == "1" || @data[match[1].to_sym] == "yes"
141
+ if (match = func_name.to_s.match(/^(\S+)\?$/)) && @data.key?(match[1].to_sym)
142
+ if @data.fetch(match[1].to_sym) == "1" || @data.fetch(match[1].to_sym) == "yes"
143
143
  return true
144
- elsif @data[match[1].to_sym] == "0" || @data[match[1].to_sym] == "no"
144
+ elsif @data.fetch(match[1].to_sym) == "0" || @data.fetch(match[1].to_sym) == "no"
145
145
  return false
146
146
  end
147
147
  end
@@ -0,0 +1,3 @@
1
+ class Baza::SqlQueries
2
+ AutoAutoloader.autoload_sub_classes(self, __FILE__)
3
+ end
@@ -0,0 +1,81 @@
1
+ class Baza::SqlQueries::GenericInsert
2
+ def initialize(args)
3
+ @db = args.fetch(:db)
4
+ @table_name = args.fetch(:table_name)
5
+ @data = args.fetch(:data)
6
+ @buffer = args[:buffer]
7
+ @return_sql = args[:return_sql]
8
+ @return_id = args[:return_id]
9
+ end
10
+
11
+ def execute
12
+ if @return_sql
13
+ to_sql
14
+ elsif @buffer
15
+ @buffer.query(to_sql)
16
+ else
17
+ @db.query(to_sql)
18
+ @db.last_id if @return_id
19
+ end
20
+ end
21
+
22
+ def to_sql
23
+ sql = "INSERT INTO #{@db.sep_table}#{@db.escape_table(@table_name)}#{@db.sep_table}"
24
+
25
+ if !@data || @data.empty?
26
+ sql << " #{sql_default_values}"
27
+ else
28
+ sql << " #{sql_columns} VALUES #{sql_values}"
29
+ end
30
+
31
+ sql
32
+ end
33
+
34
+ private
35
+
36
+ def sql_default_values
37
+ if @db.opts.fetch(:type).to_s.include?("mysql")
38
+ "VALUES ()" # This is the correct syntax for inserting a blank row in MySQL.
39
+ elsif @db.opts.fetch(:type).to_s.include?("sqlite3")
40
+ "DEFAULT VALUES"
41
+ else
42
+ raise "Unknown database-type: '#{@db.opts.fetch(:type)}'."
43
+ end
44
+ end
45
+
46
+ def sql_columns
47
+ sql = "("
48
+
49
+ first = true
50
+ @data.each_key do |key|
51
+ if first
52
+ first = false
53
+ else
54
+ sql << ", "
55
+ end
56
+
57
+ sql << "#{@db.sep_col}#{@db.escape_column(key)}#{@db.sep_col}"
58
+ end
59
+
60
+ sql << ")"
61
+ sql
62
+ end
63
+
64
+ def sql_values
65
+ sql = "("
66
+
67
+ first = true
68
+ @data.each_value do |value|
69
+ if first
70
+ first = false
71
+ else
72
+ sql << ", "
73
+ end
74
+
75
+ sql << @db.sqlval(value)
76
+ end
77
+
78
+ sql << ")"
79
+ sql
80
+ end
81
+ end
@@ -0,0 +1,31 @@
1
+ class Baza::SqlQueries::GenericUpdate
2
+ def initialize(args)
3
+ @db = args.fetch(:db)
4
+ @table_name = args.fetch(:table_name)
5
+ @data = args.fetch(:data)
6
+ @terms = args.fetch(:terms)
7
+ @buffer = args[:buffer]
8
+ end
9
+
10
+ def execute
11
+ if @buffer
12
+ @buffer.query(to_sql)
13
+ else
14
+ @db.query(to_sql)
15
+ end
16
+ end
17
+
18
+ def to_sql
19
+ sql = "UPDATE #{@db.sep_col}#{@db.escape_table(@table_name)}#{@db.sep_col} SET "
20
+
21
+ first = true
22
+ @data.each do |key, value|
23
+ sql << ", " unless first
24
+ first = false if first
25
+ sql << "#{@db.sep_col}#{@db.escape_column(key)}#{@db.sep_col} = #{@db.sqlval(value)}"
26
+ end
27
+
28
+ sql << " WHERE #{@db.sql_make_where(@terms)}" if @terms && !@terms.empty?
29
+ sql
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ class Baza::SqlQueries::MysqlUpsert
2
+ def initialize(args)
3
+ @db = args.fetch(:db)
4
+ @table_name = args.fetch(:table_name)
5
+ @updates = args.fetch(:updates)
6
+ @terms = args.fetch(:terms)
7
+ end
8
+
9
+ def execute
10
+ procedure_name = "baza_upsert_#{SecureRandom.hex(5)}"
11
+
12
+ sql = "CREATE PROCEDURE `#{@db.escape_table(procedure_name)}` () BEGIN\n"
13
+ sql << "\tIF EXISTS(#{select_query}) THEN\n"
14
+ sql << "\t\t#{update_sql};\n"
15
+ sql << "\tELSE\n"
16
+ sql << "\t\t#{insert_sql};\n"
17
+ sql << "\tEND IF;\n"
18
+ sql << "END;\n"
19
+
20
+ @db.query(sql)
21
+
22
+ begin
23
+ @db.query("CALL `#{@db.escape_table(procedure_name)}`")
24
+ ensure
25
+ @db.query("DROP PROCEDURE `#{@db.escape_table(procedure_name)}`")
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def select_query
32
+ sql = ""
33
+ sql << "SELECT * FROM #{@db.sep_table}#{@db.escape_table(@table_name)}#{@db.sep_table} WHERE"
34
+
35
+ first = true
36
+ @terms.each do |column, value|
37
+ sql << " AND" unless first
38
+ first = false if first
39
+ sql << " #{@db.sep_col}#{@db.escape_column(column)}#{@db.sep_col} = #{@db.sqlval(value)}"
40
+ end
41
+
42
+ sql
43
+ end
44
+
45
+ def update_sql
46
+ @db.update(@table_name, @updates, @terms, return_sql: true)
47
+ end
48
+
49
+ def insert_sql
50
+ @db.insert(@table_name, @updates.merge(@terms), return_sql: true)
51
+ end
52
+ end
@@ -0,0 +1,57 @@
1
+ class Baza::SqlQueries::MysqlUpsertDuplicateKey
2
+ def initialize(args)
3
+ @db = args.fetch(:db)
4
+ @table_name = args.fetch(:table_name)
5
+ @updates = StringCases.stringify_keys(args.fetch(:updates))
6
+ @terms = StringCases.stringify_keys(args.fetch(:terms))
7
+ @buffer = args[:buffer]
8
+ @return_id = args[:return_id]
9
+ end
10
+
11
+ def to_sql
12
+ sql = insert_sql
13
+ sql << " ON DUPLICATE KEY UPDATE"
14
+
15
+ first = true
16
+
17
+ if @return_id
18
+ sql << " #{@db.sep_col}#{@db.escape_column(primary_key_column_name)}#{@db.sep_col} = LAST_INSERT_ID(#{@db.sep_col}#{@db.escape_column(primary_key_column_name)}#{@db.sep_col})"
19
+ first = false
20
+ end
21
+
22
+ @updates.each do |key, value|
23
+ sql << "," unless first
24
+ first = false if first
25
+ sql << " #{@db.sep_col}#{@db.escape_column(key)}#{@db.sep_col} = #{@db.sqlval(value)}"
26
+ end
27
+
28
+ sql
29
+ end
30
+
31
+ def execute
32
+ if @buffer
33
+ @buffer.query(to_sql)
34
+ else
35
+ @db.query(to_sql)
36
+ return @db.query(last_insert_sql).fetch.fetch(:id).to_i if @return_id
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def insert_sql
43
+ @db.insert(@table_name, @updates.merge(@terms), return_sql: true)
44
+ end
45
+
46
+ def table
47
+ @table ||= @db.tables[@table_name.to_s]
48
+ end
49
+
50
+ def primary_key_column_name
51
+ @primary_key_column_name ||= table.columns(primarykey: true).first.name
52
+ end
53
+
54
+ def last_insert_sql
55
+ "SELECT LAST_INSERT_ID() AS `id` FROM #{@db.sep_table}#{@db.escape_table(@table_name)}#{@db.sep_table}"
56
+ end
57
+ end
@@ -0,0 +1,25 @@
1
+ class Baza::SqlQueries::NonAtomicUpsert
2
+ def initialize(args)
3
+ @db = args.fetch(:db)
4
+ @table_name = args.fetch(:table_name)
5
+ @updates = args.fetch(:updates)
6
+ @terms = args.fetch(:terms)
7
+ @buffer = args[:buffer]
8
+ end
9
+
10
+ def execute
11
+ row = @db.single(@table_name, @terms)
12
+
13
+ if @buffer
14
+ obj = @buffer
15
+ else
16
+ obj = @db
17
+ end
18
+
19
+ if row
20
+ obj.update(@table_name, @updates, @terms)
21
+ else
22
+ obj.insert(@table_name, @terms.merge(@updates))
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,118 @@
1
+ class Baza::SqlQueries::PostgresUpsertDuplicateKey
2
+ def initialize(args)
3
+ @db = args.fetch(:db)
4
+ @table_name = args.fetch(:table_name)
5
+ @updates = StringCases.stringify_keys(args.fetch(:updates))
6
+ @terms = StringCases.stringify_keys(args.fetch(:terms))
7
+ @return_id = args[:return_id]
8
+ end
9
+
10
+ def execute
11
+ if @db.commands.version.to_f >= 9.5
12
+ @db.query(on_conflict_sql)
13
+ elsif @terms.empty?
14
+ return insert_and_register_conflict
15
+ else
16
+ @db.query(begin_update_exception_sql)
17
+ end
18
+
19
+ @db.last_id if @return_id
20
+ end
21
+
22
+ private
23
+
24
+ def insert_and_register_conflict
25
+ @db.query(insert_sql)
26
+ @db.last_id if @return_id
27
+ rescue => e
28
+ if (match = e.message.match(/Key \((.+)\)=\((.+)\) already exists/))
29
+ column_name = match[1]
30
+ conflicting_value = match[2]
31
+
32
+ @terms = {column_name => conflicting_value}
33
+ @db.query(begin_update_exception_sql)
34
+
35
+ if @return_id
36
+ primary_column = table.columns.find(&:primarykey?).name.to_sym
37
+ data = @db.single(@table_name, column_name => conflicting_value)
38
+ return data.fetch(primary_column).to_i
39
+ end
40
+ else
41
+ raise e
42
+ end
43
+ end
44
+
45
+ def begin_update_exception_sql
46
+ sql = "do $$\n"
47
+ sql << "BEGIN\n"
48
+ sql << "\t#{insert_sql};\n"
49
+ sql << "EXCEPTION WHEN unique_violation THEN\n"
50
+ sql << "\t#{update_sql};\n"
51
+ sql << "END $$;"
52
+
53
+ sql
54
+ end
55
+
56
+ def on_conflict_sql
57
+ "#{insert_sql} ON CONFLICT DO UPDATE #{update_set_sql}"
58
+ end
59
+
60
+ def insert_sql
61
+ sql = "INSERT INTO #{@db.sep_table}#{@db.escape_table(@table_name)}#{@db.sep_table} ("
62
+
63
+ combined_data = @updates.merge(@terms)
64
+
65
+ first = true
66
+ combined_data.each_key do |column_name|
67
+ sql << ", " unless first
68
+ first = false if first
69
+ sql << "#{@db.sep_col}#{@db.escape_column(column_name)}#{@db.sep_col}"
70
+ end
71
+
72
+ sql << ") VALUES ("
73
+
74
+ first = true
75
+ combined_data.each_value do |value|
76
+ sql << ", " unless first
77
+ first = false if first
78
+ sql << @db.sqlval(value).to_s
79
+ end
80
+
81
+ sql << ")"
82
+ sql
83
+ end
84
+
85
+ def update_sql
86
+ "UPDATE #{@db.sep_table}#{@db.escape_table(@table_name)}#{@db.sep_table} #{update_set_sql} #{update_where_sql}"
87
+ end
88
+
89
+ def update_set_sql
90
+ sql = "SET "
91
+
92
+ first = true
93
+ @updates.each do |key, value|
94
+ sql << ", " unless first
95
+ first = false if first
96
+ sql << "#{@db.sep_col}#{@db.escape_column(key)}#{@db.sep_col} = #{@db.sqlval(value)}"
97
+ end
98
+
99
+ sql
100
+ end
101
+
102
+ def update_where_sql
103
+ sql = "WHERE "
104
+
105
+ first = true
106
+ @terms.each do |key, value|
107
+ sql << " AND " unless first
108
+ first = false if first
109
+ sql << "#{@db.sep_col}#{@db.escape_column(key)}#{@db.sep_col} = #{@db.sqlval(value)}"
110
+ end
111
+
112
+ sql
113
+ end
114
+
115
+ def table
116
+ @table ||= @db.tables[@table_name.to_s]
117
+ end
118
+ end