activerecord-jdbc-alt-adapter 70.2.0-java → 71.0.0.alpha2-java

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +141 -24
  3. data/.github/workflows/ruby.yml +12 -12
  4. data/.gitignore +7 -3
  5. data/.solargraph.yml +15 -0
  6. data/Gemfile +17 -4
  7. data/README.md +7 -3
  8. data/RUNNING_TESTS.md +36 -0
  9. data/activerecord-jdbc-adapter.gemspec +2 -2
  10. data/activerecord-jdbc-alt-adapter.gemspec +1 -1
  11. data/lib/arjdbc/abstract/connection_management.rb +26 -10
  12. data/lib/arjdbc/abstract/core.rb +5 -12
  13. data/lib/arjdbc/abstract/database_statements.rb +35 -25
  14. data/lib/arjdbc/abstract/statement_cache.rb +2 -7
  15. data/lib/arjdbc/abstract/transaction_support.rb +37 -22
  16. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  17. data/lib/arjdbc/jdbc/column.rb +0 -34
  18. data/lib/arjdbc/jdbc/connection_methods.rb +1 -1
  19. data/lib/arjdbc/mssql/adapter.rb +101 -79
  20. data/lib/arjdbc/mssql/column.rb +1 -0
  21. data/lib/arjdbc/mssql/connection_methods.rb +7 -55
  22. data/lib/arjdbc/mssql/database_statements.rb +182 -71
  23. data/lib/arjdbc/mssql/explain_support.rb +8 -5
  24. data/lib/arjdbc/mssql/schema_creation.rb +1 -1
  25. data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
  26. data/lib/arjdbc/mssql/schema_statements.rb +25 -14
  27. data/lib/arjdbc/mssql/server_version.rb +56 -0
  28. data/lib/arjdbc/mssql/utils.rb +23 -9
  29. data/lib/arjdbc/mysql/adapter.rb +104 -27
  30. data/lib/arjdbc/postgresql/adapter.rb +71 -44
  31. data/lib/arjdbc/postgresql/oid_types.rb +8 -27
  32. data/lib/arjdbc/postgresql/schema_statements.rb +57 -0
  33. data/lib/arjdbc/sqlite3/adapter.rb +205 -147
  34. data/lib/arjdbc/sqlite3/column.rb +103 -0
  35. data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
  36. data/lib/arjdbc/tasks/mssql_database_tasks.rb +9 -5
  37. data/lib/arjdbc/version.rb +1 -1
  38. data/rakelib/02-test.rake +1 -1
  39. data/rakelib/rails.rake +2 -0
  40. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3 -1
  41. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +11 -0
  42. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +2 -1
  43. metadata +10 -12
  44. data/lib/arel/visitors/sql_server/ng42.rb +0 -294
  45. data/lib/arel/visitors/sql_server.rb +0 -124
  46. data/lib/arjdbc/mssql/limit_helpers.rb +0 -231
  47. data/lib/arjdbc/mssql/lock_methods.rb +0 -77
  48. data/lib/arjdbc/mssql/old_adapter.rb +0 -804
  49. data/lib/arjdbc/mssql/old_column.rb +0 -200
@@ -1,231 +0,0 @@
1
- module ArJdbc
2
- module MSSQL
3
- module LimitHelpers
4
-
5
- # @private
6
- FIND_SELECT = /\b(SELECT(\s+DISTINCT)?)\b(.*)/mi
7
- # @private
8
- FIND_AGGREGATE_FUNCTION = /(AVG|COUNT|COUNT_BIG|MAX|MIN|SUM|STDDEV|STDEVP|VAR|VARP)\(/i
9
-
10
- # @private
11
- module SqlServerReplaceLimitOffset
12
-
13
- GROUP_BY = 'GROUP BY'
14
- ORDER_BY = 'ORDER BY'
15
-
16
- module_function
17
-
18
- def replace_limit_offset!(sql, limit, offset, order)
19
- offset ||= 0
20
-
21
- if match = FIND_SELECT.match(sql)
22
- select, distinct, rest_of_query = match[1], match[2], match[3]
23
- rest_of_query.strip!
24
- end
25
- rest_of_query[0] = '*' if rest_of_query[0...1] == '1' && rest_of_query !~ /1 AS/i
26
- if rest_of_query[0...1] == '*'
27
- from_table = Utils.get_table_name(rest_of_query, true)
28
- rest_of_query = "#{from_table}.#{rest_of_query}"
29
- end
30
-
31
- # Ensure correct queries if the rest_of_query contains a 'GROUP BY'. Otherwise the following error occurs:
32
- # ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: Column 'users.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
33
- # SELECT t.* FROM ( SELECT ROW_NUMBER() OVER(ORDER BY users.id) AS _row_num, [users].[lft], COUNT([users].[lft]) FROM [users] GROUP BY [users].[lft] HAVING COUNT([users].[lft]) > 1 ) AS t WHERE t._row_num BETWEEN 1 AND 1
34
- if i = ( rest_of_query.rindex(GROUP_BY) || rest_of_query.rindex('group by') )
35
- # Do not catch 'GROUP BY' statements from sub-selects, indicated
36
- # by more closing than opening brackets after the last group by.
37
- rest_after_last_group_by = rest_of_query[i..-1]
38
- opening_brackets_count = rest_after_last_group_by.count('(')
39
- closing_brackets_count = rest_after_last_group_by.count(')')
40
-
41
- if opening_brackets_count == closing_brackets_count
42
- order_start = order.strip[0, 8]; order_start.upcase!
43
- if order_start == ORDER_BY && order.match(FIND_AGGREGATE_FUNCTION)
44
- # do nothing
45
- elsif order.count(',') == 0
46
- order.gsub!(/ORDER +BY +([^\s]+)(\s+ASC|\s+DESC)?/i, 'ORDER BY MIN(\1)\2')
47
- else
48
- raise("can not handle multiple order conditions (#{order.inspect}) in #{sql.inspect}")
49
- end
50
- end
51
- end
52
-
53
- if distinct # select =~ /DISTINCT/i
54
- order = order.gsub(/(\[[a-z0-9_]+\]|[a-z0-9_]+)\./, 't.')
55
- new_sql = "SELECT t.* FROM "
56
- new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, t.* FROM (#{select} #{rest_of_query}) AS t ) AS t"
57
- append_limit_row_num_clause(new_sql, limit, offset)
58
- else
59
- select_columns_before_from = rest_of_query.gsub(/FROM.*/, '').strip
60
- only_one_column = !select_columns_before_from.include?(',')
61
- only_one_id_column = only_one_column && (select_columns_before_from.ends_with?('.id') || select_columns_before_from.ends_with?('.[id]'))
62
-
63
- if only_one_id_column
64
- # If there's only one id column a subquery will be created which only contains this column
65
- new_sql = "#{select} t.id FROM "
66
- else
67
- # All selected columns are used
68
- new_sql = "#{select} t.* FROM "
69
- end
70
- new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query} ) AS t"
71
- append_limit_row_num_clause(new_sql, limit, offset)
72
- end
73
-
74
- sql.replace new_sql
75
- end
76
-
77
- def append_limit_row_num_clause(sql, limit, offset)
78
- if limit
79
- start_row = offset + 1; end_row = offset + limit.to_i
80
- sql << " WHERE t._row_num BETWEEN #{start_row} AND #{end_row}"
81
- else
82
- sql << " WHERE t._row_num > #{offset}"
83
- end
84
- end
85
-
86
- end
87
-
88
- # @private
89
- module SqlServer2000ReplaceLimitOffset
90
-
91
- module_function
92
-
93
- def replace_limit_offset!(sql, limit, offset, order)
94
- if limit
95
- offset ||= 0
96
- start_row = offset + 1
97
- end_row = offset + limit.to_i
98
-
99
- if match = FIND_SELECT.match(sql)
100
- select, distinct, rest_of_query = match[1], match[2], match[3]
101
- end
102
- #need the table name for avoiding amiguity
103
- table_name = Utils.get_table_name(sql, true)
104
- primary_key = get_primary_key(order, table_name)
105
-
106
- #I am not sure this will cover all bases. but all the tests pass
107
- if order[/ORDER/].nil?
108
- new_order = "ORDER BY #{order}, [#{table_name}].[#{primary_key}]" if order.index("#{table_name}.#{primary_key}").nil?
109
- else
110
- new_order ||= order
111
- end
112
-
113
- if (start_row == 1) && (end_row ==1)
114
- new_sql = "#{select} TOP 1 #{rest_of_query} #{new_order}"
115
- sql.replace(new_sql)
116
- else
117
- # We are in deep trouble here. SQL Server does not have any kind of OFFSET build in.
118
- # Only remaining solution is adding a where condition to be sure that the ID is not in SELECT TOP OFFSET FROM SAME_QUERY.
119
- # To do so we need to extract each part of the query to insert our additional condition in the right place.
120
- query_without_select = rest_of_query[/FROM/i=~ rest_of_query.. -1]
121
- additional_condition = "#{table_name}.#{primary_key} NOT IN (#{select} TOP #{offset} #{table_name}.#{primary_key} #{query_without_select} #{new_order})"
122
-
123
- # Extract the different parts of the query
124
- having, group_by, where, from, selection = split_sql(rest_of_query, /having/i, /group by/i, /where/i, /from/i)
125
-
126
- # Update the where part to add our additional condition
127
- if where.blank?
128
- where = "WHERE #{additional_condition}"
129
- else
130
- where = "#{where} AND #{additional_condition}"
131
- end
132
-
133
- # Replace the query to be our new customized query
134
- sql.replace("#{select} TOP #{limit} #{selection} #{from} #{where} #{group_by} #{having} #{new_order}")
135
- end
136
- end
137
- sql
138
- end
139
-
140
- # Split the rest_of_query into chunks based on regexs (applied from end of string to the beginning)
141
- # The result is an array of regexs.size+1 elements (the last one being the remaining once everything was chopped away)
142
- def split_sql(rest_of_query, *regexs)
143
- results = Array.new
144
-
145
- regexs.each do |regex|
146
- if position = (regex =~ rest_of_query)
147
- # Extract the matched string and chop the rest_of_query
148
- matched = rest_of_query[position..-1]
149
- rest_of_query = rest_of_query[0...position]
150
- else
151
- matched = nil
152
- end
153
-
154
- results << matched
155
- end
156
- results << rest_of_query
157
-
158
- results
159
- end
160
-
161
- def get_primary_key(order, table_name) # table_name might be quoted
162
- if order =~ /(\w*id\w*)/i
163
- $1
164
- else
165
- unquoted_name = Utils.unquote_table_name(table_name)
166
- model = descendants.find { |m| m.table_name == table_name || m.table_name == unquoted_name }
167
- model ? model.primary_key : 'id'
168
- end
169
- end
170
-
171
- private
172
-
173
- if ActiveRecord::VERSION::MAJOR >= 3
174
- def descendants; ::ActiveRecord::Base.descendants; end
175
- else
176
- def descendants; ::ActiveRecord::Base.send(:subclasses) end
177
- end
178
-
179
- end
180
-
181
- private
182
-
183
- if ::ActiveRecord::VERSION::MAJOR < 3
184
-
185
- def setup_limit_offset!(version = nil)
186
- if version.to_s == '2000' || sqlserver_2000?
187
- extend SqlServer2000AddLimitOffset
188
- else
189
- extend SqlServerAddLimitOffset
190
- end
191
- end
192
-
193
- else
194
-
195
- def setup_limit_offset!(version = nil); end
196
-
197
- end
198
-
199
- # @private
200
- module SqlServerAddLimitOffset
201
-
202
- # @note Only needed with (non-AREL) ActiveRecord **2.3**.
203
- # @see Arel::Visitors::SQLServer
204
- def add_limit_offset!(sql, options)
205
- if options[:limit]
206
- order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
207
- sql.sub!(/ ORDER BY.*$/i, '')
208
- SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
209
- end
210
- end
211
-
212
- end if ::ActiveRecord::VERSION::MAJOR < 3
213
-
214
- # @private
215
- module SqlServer2000AddLimitOffset
216
-
217
- # @note Only needed with (non-AREL) ActiveRecord **2.3**.
218
- # @see Arel::Visitors::SQLServer
219
- def add_limit_offset!(sql, options)
220
- if options[:limit]
221
- order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
222
- sql.sub!(/ ORDER BY.*$/i, '')
223
- SqlServer2000ReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
224
- end
225
- end
226
-
227
- end if ::ActiveRecord::VERSION::MAJOR < 3
228
-
229
- end
230
- end
231
- end
@@ -1,77 +0,0 @@
1
- require 'strscan'
2
-
3
- module ArJdbc
4
- module MSSQL
5
- module LockMethods
6
-
7
- # @private
8
- SELECT_FROM_WHERE_RE = /\A(\s*SELECT\s.*?)(\sFROM\s)(.*?)(\sWHERE\s.*|)\Z/mi
9
-
10
- # Microsoft SQL Server uses its own syntax for SELECT .. FOR UPDATE:
11
- # SELECT .. FROM table1 WITH(ROWLOCK,UPDLOCK), table2 WITH(ROWLOCK,UPDLOCK) WHERE ..
12
- #
13
- # This does in-place modification of the passed-in string.
14
- def add_lock!(sql, options)
15
- if (lock = options[:lock]) && sql =~ /\A\s*SELECT/mi
16
- # Check for and extract the :limit/:offset sub-query
17
- if sql =~ /\A(\s*SELECT t\.\* FROM \()(.*)(\) AS t WHERE t._row_num BETWEEN \d+ AND \d+\s*)\Z/m
18
- prefix, subselect, suffix = [$1, $2, $3]
19
- add_lock!(subselect, options)
20
- return sql.replace(prefix + subselect + suffix)
21
- end
22
- unless sql =~ SELECT_FROM_WHERE_RE
23
- # If you get this error, this driver probably needs to be fixed.
24
- raise NotImplementedError, "Don't know how to add_lock! to SQL statement: #{sql.inspect}"
25
- end
26
- select_clause, from_word, from_tables, where_clause = $1, $2, $3, $4
27
- with_clause = lock.is_a?(String) ? " #{lock} " : " WITH(ROWLOCK,UPDLOCK) "
28
-
29
- # Split the FROM clause into its constituent tables, and add the with clause after each one.
30
- new_from_tables = []
31
- scanner = StringScanner.new(from_tables)
32
- until scanner.eos?
33
- prev_pos = scanner.pos
34
- if scanner.scan_until(/,|(INNER\s+JOIN|CROSS\s+JOIN|(LEFT|RIGHT|FULL)(\s+OUTER)?\s+JOIN)\s+/mi)
35
- join_operand = scanner.pre_match[prev_pos..-1]
36
- join_operator = scanner.matched
37
- else
38
- join_operand = scanner.rest
39
- join_operator = ""
40
- scanner.terminate
41
- end
42
-
43
- # At this point, we have something like:
44
- # join_operand == "appointments "
45
- # join_operator == "INNER JOIN "
46
- # or:
47
- # join_operand == "appointment_details AS d1 ON appointments.[id] = d1.[appointment_id]"
48
- # join_operator == ""
49
- if join_operand =~ /\A(.*)(\s+ON\s+.*)\Z/mi
50
- table_spec, on_clause = $1, $2
51
- else
52
- table_spec = join_operand
53
- on_clause = ""
54
- end
55
-
56
- # Add the "WITH(ROWLOCK,UPDLOCK)" option to the table specification
57
- table_spec << with_clause unless table_spec =~ /\A\(\s*SELECT\s+/mi # HACK - this parser isn't so great
58
- join_operand = table_spec + on_clause
59
-
60
- # So now we have something like:
61
- # join_operand == "appointments WITH(ROWLOCK,UPDLOCK) "
62
- # join_operator == "INNER JOIN "
63
- # or:
64
- # join_operand == "appointment_details AS d1 WITH(ROWLOCK,UPDLOCK) ON appointments.[id] = d1.[appointment_id]"
65
- # join_operator == ""
66
-
67
- new_from_tables << join_operand
68
- new_from_tables << join_operator
69
- end
70
- sql.replace( select_clause.to_s << from_word.to_s << new_from_tables.join << where_clause.to_s )
71
- end
72
- sql
73
- end
74
-
75
- end
76
- end
77
- end