activerecord-jdbc-adapter-onsite 1.2.2

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 (228) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +14 -0
  3. data/Appraisals +16 -0
  4. data/Gemfile +11 -0
  5. data/Gemfile.lock +45 -0
  6. data/History.txt +488 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.rdoc +214 -0
  9. data/Rakefile +62 -0
  10. data/activerecord-jdbc-adapter.gemspec +23 -0
  11. data/bench/bench_attributes.rb +13 -0
  12. data/bench/bench_attributes_new.rb +14 -0
  13. data/bench/bench_create.rb +12 -0
  14. data/bench/bench_find_all.rb +12 -0
  15. data/bench/bench_find_all_mt.rb +25 -0
  16. data/bench/bench_model.rb +85 -0
  17. data/bench/bench_new.rb +12 -0
  18. data/bench/bench_new_valid.rb +12 -0
  19. data/bench/bench_valid.rb +13 -0
  20. data/gemfiles/rails23.gemfile +10 -0
  21. data/gemfiles/rails23.gemfile.lock +38 -0
  22. data/gemfiles/rails30.gemfile +9 -0
  23. data/gemfiles/rails30.gemfile.lock +33 -0
  24. data/gemfiles/rails31.gemfile +9 -0
  25. data/gemfiles/rails31.gemfile.lock +35 -0
  26. data/gemfiles/rails32.gemfile +9 -0
  27. data/gemfiles/rails32.gemfile.lock +35 -0
  28. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  32. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  33. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  34. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  36. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  37. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  39. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  40. data/lib/activerecord-jdbc-adapter.rb +8 -0
  41. data/lib/arel/engines/sql/compilers/db2_compiler.rb +9 -0
  42. data/lib/arel/engines/sql/compilers/derby_compiler.rb +6 -0
  43. data/lib/arel/engines/sql/compilers/h2_compiler.rb +6 -0
  44. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +15 -0
  45. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
  46. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +46 -0
  47. data/lib/arel/visitors/compat.rb +13 -0
  48. data/lib/arel/visitors/db2.rb +17 -0
  49. data/lib/arel/visitors/derby.rb +32 -0
  50. data/lib/arel/visitors/firebird.rb +24 -0
  51. data/lib/arel/visitors/hsqldb.rb +26 -0
  52. data/lib/arel/visitors/sql_server.rb +46 -0
  53. data/lib/arjdbc.rb +24 -0
  54. data/lib/arjdbc/db2.rb +2 -0
  55. data/lib/arjdbc/db2/adapter.rb +541 -0
  56. data/lib/arjdbc/derby.rb +7 -0
  57. data/lib/arjdbc/derby/adapter.rb +358 -0
  58. data/lib/arjdbc/derby/connection_methods.rb +19 -0
  59. data/lib/arjdbc/discover.rb +92 -0
  60. data/lib/arjdbc/firebird.rb +2 -0
  61. data/lib/arjdbc/firebird/adapter.rb +140 -0
  62. data/lib/arjdbc/h2.rb +4 -0
  63. data/lib/arjdbc/h2/adapter.rb +54 -0
  64. data/lib/arjdbc/h2/connection_methods.rb +13 -0
  65. data/lib/arjdbc/hsqldb.rb +4 -0
  66. data/lib/arjdbc/hsqldb/adapter.rb +184 -0
  67. data/lib/arjdbc/hsqldb/connection_methods.rb +15 -0
  68. data/lib/arjdbc/informix.rb +3 -0
  69. data/lib/arjdbc/informix/adapter.rb +142 -0
  70. data/lib/arjdbc/informix/connection_methods.rb +11 -0
  71. data/lib/arjdbc/jdbc.rb +2 -0
  72. data/lib/arjdbc/jdbc/adapter.rb +356 -0
  73. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  74. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  75. data/lib/arjdbc/jdbc/callbacks.rb +44 -0
  76. data/lib/arjdbc/jdbc/column.rb +47 -0
  77. data/lib/arjdbc/jdbc/compatibility.rb +51 -0
  78. data/lib/arjdbc/jdbc/connection.rb +134 -0
  79. data/lib/arjdbc/jdbc/connection_methods.rb +16 -0
  80. data/lib/arjdbc/jdbc/core_ext.rb +24 -0
  81. data/lib/arjdbc/jdbc/discover.rb +18 -0
  82. data/lib/arjdbc/jdbc/driver.rb +35 -0
  83. data/lib/arjdbc/jdbc/extension.rb +47 -0
  84. data/lib/arjdbc/jdbc/java.rb +14 -0
  85. data/lib/arjdbc/jdbc/jdbc.rake +131 -0
  86. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +88 -0
  87. data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
  88. data/lib/arjdbc/jdbc/railtie.rb +9 -0
  89. data/lib/arjdbc/jdbc/rake_tasks.rb +10 -0
  90. data/lib/arjdbc/jdbc/require_driver.rb +16 -0
  91. data/lib/arjdbc/jdbc/type_converter.rb +126 -0
  92. data/lib/arjdbc/mimer.rb +2 -0
  93. data/lib/arjdbc/mimer/adapter.rb +142 -0
  94. data/lib/arjdbc/mssql.rb +4 -0
  95. data/lib/arjdbc/mssql/adapter.rb +477 -0
  96. data/lib/arjdbc/mssql/connection_methods.rb +31 -0
  97. data/lib/arjdbc/mssql/limit_helpers.rb +101 -0
  98. data/lib/arjdbc/mssql/lock_helpers.rb +72 -0
  99. data/lib/arjdbc/mssql/tsql_helper.rb +61 -0
  100. data/lib/arjdbc/mysql.rb +4 -0
  101. data/lib/arjdbc/mysql/adapter.rb +505 -0
  102. data/lib/arjdbc/mysql/connection_methods.rb +28 -0
  103. data/lib/arjdbc/oracle.rb +3 -0
  104. data/lib/arjdbc/oracle/adapter.rb +432 -0
  105. data/lib/arjdbc/oracle/connection_methods.rb +12 -0
  106. data/lib/arjdbc/postgresql.rb +4 -0
  107. data/lib/arjdbc/postgresql/adapter.rb +861 -0
  108. data/lib/arjdbc/postgresql/connection_methods.rb +23 -0
  109. data/lib/arjdbc/sqlite3.rb +4 -0
  110. data/lib/arjdbc/sqlite3/adapter.rb +389 -0
  111. data/lib/arjdbc/sqlite3/connection_methods.rb +35 -0
  112. data/lib/arjdbc/sybase.rb +2 -0
  113. data/lib/arjdbc/sybase/adapter.rb +46 -0
  114. data/lib/arjdbc/version.rb +8 -0
  115. data/lib/generators/jdbc/USAGE +10 -0
  116. data/lib/generators/jdbc/jdbc_generator.rb +9 -0
  117. data/lib/jdbc_adapter.rb +2 -0
  118. data/lib/jdbc_adapter/rake_tasks.rb +3 -0
  119. data/lib/jdbc_adapter/version.rb +3 -0
  120. data/lib/pg.rb +26 -0
  121. data/pom.xml +57 -0
  122. data/rails_generators/jdbc_generator.rb +15 -0
  123. data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
  124. data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
  125. data/rakelib/bundler_ext.rb +11 -0
  126. data/rakelib/compile.rake +23 -0
  127. data/rakelib/db.rake +39 -0
  128. data/rakelib/rails.rake +41 -0
  129. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +69 -0
  130. data/src/java/arjdbc/derby/DerbyModule.java +324 -0
  131. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +70 -0
  132. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +74 -0
  133. data/src/java/arjdbc/jdbc/AdapterJavaService.java +68 -0
  134. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +36 -0
  135. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1346 -0
  136. data/src/java/arjdbc/jdbc/SQLBlock.java +48 -0
  137. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +127 -0
  138. data/src/java/arjdbc/mysql/MySQLModule.java +134 -0
  139. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +161 -0
  140. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
  141. data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +82 -0
  142. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +126 -0
  143. data/test/abstract_db_create.rb +135 -0
  144. data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
  145. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
  146. data/test/activerecord/jall.sh +7 -0
  147. data/test/activerecord/jtest.sh +3 -0
  148. data/test/db/db2.rb +11 -0
  149. data/test/db/derby.rb +12 -0
  150. data/test/db/h2.rb +11 -0
  151. data/test/db/hsqldb.rb +13 -0
  152. data/test/db/informix.rb +11 -0
  153. data/test/db/jdbc.rb +12 -0
  154. data/test/db/jndi_config.rb +40 -0
  155. data/test/db/logger.rb +3 -0
  156. data/test/db/mssql.rb +9 -0
  157. data/test/db/mysql.rb +10 -0
  158. data/test/db/oracle.rb +34 -0
  159. data/test/db/postgres.rb +18 -0
  160. data/test/db/sqlite3.rb +11 -0
  161. data/test/db2_reset_column_information_test.rb +8 -0
  162. data/test/db2_simple_test.rb +66 -0
  163. data/test/derby_migration_test.rb +68 -0
  164. data/test/derby_multibyte_test.rb +12 -0
  165. data/test/derby_reset_column_information_test.rb +8 -0
  166. data/test/derby_row_locking_test.rb +9 -0
  167. data/test/derby_simple_test.rb +139 -0
  168. data/test/generic_jdbc_connection_test.rb +29 -0
  169. data/test/h2_change_column_test.rb +68 -0
  170. data/test/h2_simple_test.rb +41 -0
  171. data/test/has_many_through.rb +79 -0
  172. data/test/helper.rb +108 -0
  173. data/test/hsqldb_simple_test.rb +6 -0
  174. data/test/informix_simple_test.rb +48 -0
  175. data/test/jdbc_common.rb +28 -0
  176. data/test/jndi_callbacks_test.rb +36 -0
  177. data/test/jndi_test.rb +25 -0
  178. data/test/manualTestDatabase.rb +191 -0
  179. data/test/models/add_not_null_column_to_table.rb +9 -0
  180. data/test/models/auto_id.rb +15 -0
  181. data/test/models/custom_pk_name.rb +14 -0
  182. data/test/models/data_types.rb +30 -0
  183. data/test/models/entry.rb +40 -0
  184. data/test/models/mixed_case.rb +22 -0
  185. data/test/models/reserved_word.rb +15 -0
  186. data/test/models/string_id.rb +17 -0
  187. data/test/models/thing.rb +16 -0
  188. data/test/models/validates_uniqueness_of_string.rb +19 -0
  189. data/test/mssql_db_create_test.rb +26 -0
  190. data/test/mssql_identity_insert_test.rb +19 -0
  191. data/test/mssql_ignore_system_views_test.rb +27 -0
  192. data/test/mssql_legacy_types_test.rb +58 -0
  193. data/test/mssql_limit_offset_test.rb +136 -0
  194. data/test/mssql_multibyte_test.rb +18 -0
  195. data/test/mssql_null_test.rb +14 -0
  196. data/test/mssql_reset_column_information_test.rb +8 -0
  197. data/test/mssql_row_locking_sql_test.rb +159 -0
  198. data/test/mssql_row_locking_test.rb +9 -0
  199. data/test/mssql_simple_test.rb +55 -0
  200. data/test/mysql_db_create_test.rb +27 -0
  201. data/test/mysql_index_length_test.rb +58 -0
  202. data/test/mysql_info_test.rb +123 -0
  203. data/test/mysql_multibyte_test.rb +10 -0
  204. data/test/mysql_nonstandard_primary_key_test.rb +42 -0
  205. data/test/mysql_reset_column_information_test.rb +8 -0
  206. data/test/mysql_simple_test.rb +125 -0
  207. data/test/oracle_reset_column_information_test.rb +8 -0
  208. data/test/oracle_simple_test.rb +18 -0
  209. data/test/oracle_specific_test.rb +83 -0
  210. data/test/postgres_db_create_test.rb +32 -0
  211. data/test/postgres_drop_db_test.rb +16 -0
  212. data/test/postgres_information_schema_leak_test.rb +29 -0
  213. data/test/postgres_mixed_case_test.rb +29 -0
  214. data/test/postgres_native_type_mapping_test.rb +93 -0
  215. data/test/postgres_nonseq_pkey_test.rb +38 -0
  216. data/test/postgres_reserved_test.rb +22 -0
  217. data/test/postgres_reset_column_information_test.rb +8 -0
  218. data/test/postgres_schema_search_path_test.rb +48 -0
  219. data/test/postgres_simple_test.rb +168 -0
  220. data/test/postgres_table_alias_length_test.rb +15 -0
  221. data/test/postgres_type_conversion_test.rb +34 -0
  222. data/test/row_locking.rb +90 -0
  223. data/test/simple.rb +731 -0
  224. data/test/sqlite3_reset_column_information_test.rb +8 -0
  225. data/test/sqlite3_simple_test.rb +316 -0
  226. data/test/sybase_jtds_simple_test.rb +28 -0
  227. data/test/sybase_reset_column_information_test.rb +8 -0
  228. metadata +288 -0
@@ -0,0 +1,31 @@
1
+ class ActiveRecord::Base
2
+ class << self
3
+ def mssql_connection(config)
4
+ require "arjdbc/mssql"
5
+ config[:host] ||= "localhost"
6
+ config[:port] ||= 1433
7
+ config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
8
+ config[:adapter_spec] = ::ArJdbc::MsSQL
9
+
10
+ url = "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
11
+
12
+ # Instance is often a preferrable alternative to port when dynamic ports are used.
13
+ # If instance is specified then port is essentially ignored.
14
+ url << ";instance=#{config[:instance]}" if config[:instance]
15
+
16
+ # This will enable windows domain-based authentication and will require the JTDS native libraries be available.
17
+ url << ";domain=#{config[:domain]}" if config[:domain]
18
+
19
+ # AppName is shown in sql server as additional information against the connection.
20
+ url << ";appname=#{config[:appname]}" if config[:appname]
21
+ config[:url] ||= url
22
+
23
+ if !config[:domain]
24
+ config[:username] ||= "sa"
25
+ config[:password] ||= ""
26
+ end
27
+ jdbc_connection(config)
28
+ end
29
+ alias_method :jdbcmssql_connection, :mssql_connection
30
+ end
31
+ end
@@ -0,0 +1,101 @@
1
+ module ::ArJdbc
2
+ module MsSQL
3
+ module LimitHelpers
4
+ module_function
5
+ def get_table_name(sql)
6
+ if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
7
+ $1
8
+ elsif sql =~ /\bfrom\s+([^\(\s,]+)\s*/i
9
+ $1
10
+ else
11
+ nil
12
+ end
13
+ end
14
+
15
+ module SqlServer2000ReplaceLimitOffset
16
+ module_function
17
+ def replace_limit_offset!(sql, limit, offset, order)
18
+ if limit
19
+ offset ||= 0
20
+ start_row = offset + 1
21
+ end_row = offset + limit.to_i
22
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
23
+ whole, select, rest_of_query = find_select.match(sql).to_a
24
+ if (start_row == 1) && (end_row ==1)
25
+ new_sql = "#{select} TOP 1 #{rest_of_query}"
26
+ sql.replace(new_sql)
27
+ else
28
+ #UGLY
29
+ #KLUDGY?
30
+ #removing out stuff before the FROM...
31
+ rest = rest_of_query[/FROM/i=~ rest_of_query.. -1]
32
+ #need the table name for avoiding amiguity
33
+ table_name = LimitHelpers.get_table_name(sql)
34
+ primary_key = order[/(\w*id\w*)/i] || "id"
35
+ #I am not sure this will cover all bases. but all the tests pass
36
+ if order[/ORDER/].nil?
37
+ new_order = "ORDER BY #{order}, #{table_name}.#{primary_key}" if order.index("#{table_name}.#{primary_key}").nil?
38
+ else
39
+ new_order ||= order
40
+ end
41
+
42
+ if (rest_of_query.match(/WHERE/).nil?)
43
+ new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.#{primary_key} NOT IN (#{select} TOP #{offset} #{table_name}.#{primary_key} #{rest} #{new_order}) #{order} "
44
+ else
45
+ new_sql = "#{select} TOP #{limit} #{rest_of_query} AND #{table_name}.#{primary_key} NOT IN (#{select} TOP #{offset} #{table_name}.#{primary_key} #{rest} #{new_order}) #{order} "
46
+ end
47
+
48
+ sql.replace(new_sql)
49
+ end
50
+ end
51
+ sql
52
+ end
53
+ end
54
+
55
+ module SqlServer2000AddLimitOffset
56
+ def add_limit_offset!(sql, options)
57
+ if options[:limit]
58
+ order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
59
+ sql.sub!(/ ORDER BY.*$/i, '')
60
+ SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
61
+ end
62
+ end
63
+ end
64
+
65
+ module SqlServerReplaceLimitOffset
66
+ module_function
67
+ def replace_limit_offset!(sql, limit, offset, order)
68
+ if limit
69
+ offset ||= 0
70
+ start_row = offset + 1
71
+ end_row = offset + limit.to_i
72
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
73
+ whole, select, rest_of_query = find_select.match(sql).to_a
74
+ rest_of_query.strip!
75
+ if rest_of_query[0...1] == "1" && rest_of_query !~ /1 AS/i
76
+ rest_of_query[0] = "*"
77
+ end
78
+ if rest_of_query[0] == "*"
79
+ from_table = LimitHelpers.get_table_name(rest_of_query)
80
+ rest_of_query = from_table + '.' + rest_of_query
81
+ end
82
+ new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query}"
83
+ new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
84
+ sql.replace(new_sql)
85
+ end
86
+ sql
87
+ end
88
+ end
89
+
90
+ module SqlServerAddLimitOffset
91
+ def add_limit_offset!(sql, options)
92
+ if options[:limit]
93
+ order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
94
+ sql.sub!(/ ORDER BY.*$/i, '')
95
+ SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,72 @@
1
+ module ::ArJdbc
2
+ module MsSQL
3
+ module LockHelpers
4
+ module SqlServerAddLock
5
+ # Microsoft SQL Server uses its own syntax for SELECT .. FOR UPDATE:
6
+ # SELECT .. FROM table1 WITH(ROWLOCK,UPDLOCK), table2 WITH(ROWLOCK,UPDLOCK) WHERE ..
7
+ #
8
+ # This does in-place modification of the passed-in string.
9
+ def add_lock!(sql, options)
10
+ if options[:lock] and sql =~ /\A\s*SELECT/mi
11
+ # Check for and extract the :limit/:offset sub-query
12
+ if sql =~ /\A(\s*SELECT t\.\* FROM \()(.*)(\) AS t WHERE t._row_num BETWEEN \d+ AND \d+\s*)\Z/m
13
+ prefix, subselect, suffix = [$1, $2, $3]
14
+ add_lock!(subselect, options)
15
+ return sql.replace(prefix + subselect + suffix)
16
+ end
17
+ unless sql =~ /\A(\s*SELECT\s.*?)(\sFROM\s)(.*?)(\sWHERE\s.*|)\Z/mi
18
+ # If you get this error, this driver probably needs to be fixed.
19
+ raise NotImplementedError, "Don't know how to add_lock! to SQL statement: #{sql.inspect}"
20
+ end
21
+ select_clause, from_word, from_tables, where_clause = [$1, $2, $3, $4]
22
+ with_clause = options[:lock].is_a?(String) ? " #{options[:lock]} " : " WITH(ROWLOCK,UPDLOCK) "
23
+
24
+ # Split the FROM clause into its constituent tables, and add the with clause after each one.
25
+ new_from_tables = []
26
+ s = StringScanner.new(from_tables)
27
+ until s.eos?
28
+ prev_pos = s.pos
29
+ if s.scan_until(/,|(INNER\s+JOIN|CROSS\s+JOIN|(LEFT|RIGHT|FULL)(\s+OUTER)?\s+JOIN)\s+/mi)
30
+ join_operand = s.pre_match[prev_pos..-1]
31
+ join_operator = s.matched
32
+ else
33
+ join_operand = s.rest
34
+ join_operator = ""
35
+ s.terminate
36
+ end
37
+
38
+ # At this point, we have something like:
39
+ # join_operand == "appointments "
40
+ # join_operator == "INNER JOIN "
41
+ # or:
42
+ # join_operand == "appointment_details AS d1 ON appointments.[id] = d1.[appointment_id]"
43
+ # join_operator == ""
44
+ if join_operand =~ /\A(.*)(\s+ON\s+.*)\Z/mi
45
+ table_spec, on_clause = [$1, $2]
46
+ else
47
+ table_spec = join_operand
48
+ on_clause = ""
49
+ end
50
+
51
+ # Add the "WITH(ROWLOCK,UPDLOCK)" option to the table specification
52
+ table_spec << with_clause unless table_spec =~ /\A\(\s*SELECT\s+/mi # HACK - this parser isn't so great
53
+ join_operand = table_spec + on_clause
54
+
55
+ # So now we have something like:
56
+ # join_operand == "appointments WITH(ROWLOCK,UPDLOCK) "
57
+ # join_operator == "INNER JOIN "
58
+ # or:
59
+ # join_operand == "appointment_details AS d1 WITH(ROWLOCK,UPDLOCK) ON appointments.[id] = d1.[appointment_id]"
60
+ # join_operator == ""
61
+
62
+ new_from_tables << join_operand
63
+ new_from_tables << join_operator
64
+ end
65
+ sql.replace([select_clause, from_word, new_from_tables, where_clause].flatten.join)
66
+ end
67
+ sql
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,61 @@
1
+ # Common methods for handling TSQL databases.
2
+ module TSqlMethods
3
+
4
+ def modify_types(tp) #:nodoc:
5
+ tp[:primary_key] = "int NOT NULL IDENTITY(1, 1) PRIMARY KEY"
6
+ tp[:integer][:limit] = nil
7
+ tp[:boolean] = {:name => "bit"}
8
+ tp[:binary] = { :name => "image"}
9
+ tp
10
+ end
11
+
12
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
13
+ limit = nil if %w(text binary).include? type.to_s
14
+ return 'uniqueidentifier' if (type.to_s == 'uniqueidentifier')
15
+ return super unless type.to_s == 'integer'
16
+
17
+ if limit.nil? || limit == 4
18
+ 'int'
19
+ elsif limit == 2
20
+ 'smallint'
21
+ elsif limit == 1
22
+ 'tinyint'
23
+ else
24
+ 'bigint'
25
+ end
26
+ end
27
+
28
+ def add_limit_offset!(sql, options)
29
+ if options[:limit] and options[:offset]
30
+ total_rows = select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT\\1 TOP 1000000000")}) tally")[0]["TotalRows"].to_i
31
+ if (options[:limit] + options[:offset]) >= total_rows
32
+ options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
33
+ end
34
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT\\1 TOP #{options[:limit] + options[:offset]} ")
35
+ sql << ") AS tmp1"
36
+ if options[:order]
37
+ options[:order] = options[:order].split(',').map do |field|
38
+ parts = field.split(" ")
39
+ tc = parts[0]
40
+ if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
41
+ tc.gsub!(/\./, '\\.\\[')
42
+ tc << '\\]'
43
+ end
44
+ if sql =~ /#{tc} AS (t\d_r\d\d?)/
45
+ parts[0] = $1
46
+ elsif parts[0] =~ /\w+\.(\w+)/
47
+ parts[0] = $1
48
+ end
49
+ parts.join(' ')
50
+ end.join(', ')
51
+ sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
52
+ else
53
+ sql << " ) AS tmp2"
54
+ end
55
+ elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
56
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
57
+ "SELECT#{$1} TOP #{options[:limit]}"
58
+ end unless options[:limit].nil?
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,4 @@
1
+ require 'arjdbc/jdbc'
2
+ jdbc_require_driver 'jdbc/mysql'
3
+ require 'arjdbc/mysql/connection_methods'
4
+ require 'arjdbc/mysql/adapter'
@@ -0,0 +1,505 @@
1
+ require 'active_record/connection_adapters/abstract/schema_definitions'
2
+
3
+ module ::ArJdbc
4
+ module MySQL
5
+ def self.column_selector
6
+ [/mysql/i, lambda {|cfg,col| col.extend(::ArJdbc::MySQL::ColumnExtensions)}]
7
+ end
8
+
9
+ def self.extended(adapter)
10
+ adapter.configure_connection
11
+ end
12
+
13
+ def configure_connection
14
+ execute("SET SQL_AUTO_IS_NULL=0")
15
+ end
16
+
17
+ def self.jdbc_connection_class
18
+ ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
19
+ end
20
+
21
+ module ColumnExtensions
22
+ def extract_default(default)
23
+ if sql_type =~ /blob/i || type == :text
24
+ if default.blank?
25
+ return null ? nil : ''
26
+ else
27
+ raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
28
+ end
29
+ elsif missing_default_forged_as_empty_string?(default)
30
+ nil
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def has_default?
37
+ return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
38
+ super
39
+ end
40
+
41
+ def simplified_type(field_type)
42
+ case field_type
43
+ when /tinyint\(1\)|bit/i then :boolean
44
+ when /enum/i then :string
45
+ when /year/i then :integer
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def extract_limit(sql_type)
52
+ case sql_type
53
+ when /blob|text/i
54
+ case sql_type
55
+ when /tiny/i
56
+ 255
57
+ when /medium/i
58
+ 16777215
59
+ when /long/i
60
+ 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
61
+ else
62
+ nil # we could return 65535 here, but we leave it undecorated by default
63
+ end
64
+ when /^enum/i; 255
65
+ when /^bigint/i; 8
66
+ when /^int/i; 4
67
+ when /^mediumint/i; 3
68
+ when /^smallint/i; 2
69
+ when /^tinyint/i; 1
70
+ when /^(bool|date|float|int|time)/i
71
+ nil
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ # MySQL misreports NOT NULL column default when none is given.
78
+ # We can't detect this for columns which may have a legitimate ''
79
+ # default (string) but we can for others (integer, datetime, boolean,
80
+ # and the rest).
81
+ #
82
+ # Test whether the column has default '', is not null, and is not
83
+ # a type allowing default ''.
84
+ def missing_default_forged_as_empty_string?(default)
85
+ type != :string && !null && default == ''
86
+ end
87
+ end
88
+
89
+ def modify_types(tp)
90
+ tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
91
+ tp[:integer] = { :name => 'int', :limit => 4 }
92
+ tp[:decimal] = { :name => "decimal" }
93
+ tp[:timestamp] = { :name => "datetime" }
94
+ tp[:datetime][:limit] = nil
95
+ tp
96
+ end
97
+
98
+ def adapter_name #:nodoc:
99
+ 'MySQL'
100
+ end
101
+
102
+ def self.arel2_visitors(config)
103
+ {}.tap {|v| %w(mysql mysql2 jdbcmysql).each {|a| v[a] = ::Arel::Visitors::MySQL } }
104
+ end
105
+
106
+ def case_sensitive_equality_operator
107
+ "= BINARY"
108
+ end
109
+
110
+ def case_sensitive_modifier(node)
111
+ Arel::Nodes::Bin.new(node)
112
+ end
113
+
114
+ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
115
+ where_sql
116
+ end
117
+
118
+ # QUOTING ==================================================
119
+
120
+ def quote(value, column = nil)
121
+ return value.quoted_id if value.respond_to?(:quoted_id)
122
+
123
+ if column && column.type == :primary_key
124
+ value.to_s
125
+ elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
126
+ s = column.class.string_to_binary(value).unpack("H*")[0]
127
+ "x'#{s}'"
128
+ elsif BigDecimal === value
129
+ "'#{value.to_s("F")}'"
130
+ else
131
+ super
132
+ end
133
+ end
134
+
135
+ def quote_column_name(name)
136
+ "`#{name.to_s.gsub('`', '``')}`"
137
+ end
138
+
139
+ def quoted_true
140
+ "1"
141
+ end
142
+
143
+ def quoted_false
144
+ "0"
145
+ end
146
+
147
+ def supports_savepoints? #:nodoc:
148
+ true
149
+ end
150
+
151
+ def create_savepoint
152
+ execute("SAVEPOINT #{current_savepoint_name}")
153
+ end
154
+
155
+ def rollback_to_savepoint
156
+ execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
157
+ end
158
+
159
+ def release_savepoint
160
+ execute("RELEASE SAVEPOINT #{current_savepoint_name}")
161
+ end
162
+
163
+ def disable_referential_integrity(&block) #:nodoc:
164
+ old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
165
+ begin
166
+ update("SET FOREIGN_KEY_CHECKS = 0")
167
+ yield
168
+ ensure
169
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
170
+ end
171
+ end
172
+
173
+ # SCHEMA STATEMENTS ========================================
174
+
175
+ def structure_dump #:nodoc:
176
+ if supports_views?
177
+ sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
178
+ else
179
+ sql = "SHOW TABLES"
180
+ end
181
+
182
+ select_all(sql).inject("") do |structure, table|
183
+ table.delete('Table_type')
184
+
185
+ hash = show_create_table(table.to_a.first.last)
186
+
187
+ if(table = hash["Create Table"])
188
+ structure += table + ";\n\n"
189
+ elsif(view = hash["Create View"])
190
+ structure += view + ";\n\n"
191
+ end
192
+ end
193
+ end
194
+
195
+ # based on:
196
+ # https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L756
197
+ # Required for passing rails column caching tests
198
+ # Returns a table's primary key and belonging sequence.
199
+ def pk_and_sequence_for(table) #:nodoc:
200
+ keys = []
201
+ result = execute("SHOW INDEX FROM #{quote_table_name(table)} WHERE Key_name = 'PRIMARY'", 'SCHEMA')
202
+ result.each do |h|
203
+ keys << h["Column_name"]
204
+ end
205
+ keys.length == 1 ? [keys.first, nil] : nil
206
+ end
207
+
208
+ # based on:
209
+ # https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L647
210
+ # Returns an array of indexes for the given table.
211
+ def indexes(table_name, name = nil)#:nodoc:
212
+ indexes = []
213
+ current_index = nil
214
+ result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
215
+ result.each do |row|
216
+ key_name = row["Key_name"]
217
+ if current_index != key_name
218
+ next if key_name == "PRIMARY" # skip the primary key
219
+ current_index = key_name
220
+ indexes << ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(
221
+ row["Table"], key_name, row["Non_unique"] == 0, [], [])
222
+ end
223
+
224
+ indexes.last.columns << row["Column_name"]
225
+ indexes.last.lengths << row["Sub_part"]
226
+ end
227
+ indexes
228
+ end
229
+
230
+ def jdbc_columns(table_name, name = nil)#:nodoc:
231
+ sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
232
+ execute(sql, 'SCHEMA').map do |field|
233
+ ::ActiveRecord::ConnectionAdapters::MysqlColumn.new(field["Field"], field["Default"], field["Type"], field["Null"] == "YES")
234
+ end
235
+ end
236
+
237
+ # Returns just a table's primary key
238
+ def primary_key(table)
239
+ pk_and_sequence = pk_and_sequence_for(table)
240
+ pk_and_sequence && pk_and_sequence.first
241
+ end
242
+
243
+ def recreate_database(name, options = {}) #:nodoc:
244
+ drop_database(name)
245
+ create_database(name, options)
246
+ end
247
+
248
+ def create_database(name, options = {}) #:nodoc:
249
+ if options[:collation]
250
+ execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
251
+ else
252
+ execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
253
+ end
254
+ end
255
+
256
+ def drop_database(name) #:nodoc:
257
+ execute "DROP DATABASE IF EXISTS `#{name}`"
258
+ end
259
+
260
+ def current_database
261
+ select_one("SELECT DATABASE() as db")["db"]
262
+ end
263
+
264
+ def create_table(name, options = {}) #:nodoc:
265
+ super(name, {:options => "ENGINE=InnoDB DEFAULT CHARSET=utf8"}.merge(options))
266
+ end
267
+
268
+ def rename_table(name, new_name)
269
+ execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
270
+ end
271
+
272
+ def add_column(table_name, column_name, type, options = {})
273
+ add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
274
+ add_column_options!(add_column_sql, options)
275
+ add_column_position!(add_column_sql, options)
276
+ execute(add_column_sql)
277
+ end
278
+
279
+ def change_column_default(table_name, column_name, default) #:nodoc:
280
+ column = column_for(table_name, column_name)
281
+ change_column table_name, column_name, column.sql_type, :default => default
282
+ end
283
+
284
+ def change_column_null(table_name, column_name, null, default = nil)
285
+ column = column_for(table_name, column_name)
286
+
287
+ unless null || default.nil?
288
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
289
+ end
290
+
291
+ change_column table_name, column_name, column.sql_type, :null => null
292
+ end
293
+
294
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
295
+ column = column_for(table_name, column_name)
296
+
297
+ unless options_include_default?(options)
298
+ options[:default] = column.default
299
+ end
300
+
301
+ unless options.has_key?(:null)
302
+ options[:null] = column.null
303
+ end
304
+
305
+ change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
306
+ add_column_options!(change_column_sql, options)
307
+ add_column_position!(change_column_sql, options)
308
+ execute(change_column_sql)
309
+ end
310
+
311
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
312
+ options = {}
313
+ if column = columns(table_name).find { |c| c.name == column_name.to_s }
314
+ options[:default] = column.default
315
+ options[:null] = column.null
316
+ else
317
+ raise ActiveRecord::ActiveRecordError, "No such column: #{table_name}.#{column_name}"
318
+ end
319
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
320
+ rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
321
+ add_column_options!(rename_column_sql, options)
322
+ execute(rename_column_sql)
323
+ end
324
+
325
+ def add_limit_offset!(sql, options) #:nodoc:
326
+ limit, offset = options[:limit], options[:offset]
327
+ if limit && offset
328
+ sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
329
+ elsif limit
330
+ sql << " LIMIT #{sanitize_limit(limit)}"
331
+ elsif offset
332
+ sql << " OFFSET #{offset.to_i}"
333
+ end
334
+ sql
335
+ end
336
+
337
+ # Taken from: https://github.com/gfmurphy/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb#L540
338
+ #
339
+ # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
340
+ # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
341
+ # these, we must use a subquery. However, MySQL is too stupid to create a
342
+ # temporary table for this automatically, so we have to give it some prompting
343
+ # in the form of a subsubquery. Ugh!
344
+ def join_to_update(update, select) #:nodoc:
345
+ if select.limit || select.offset || select.orders.any?
346
+ subsubselect = select.clone
347
+ subsubselect.projections = [update.key]
348
+
349
+ subselect = Arel::SelectManager.new(select.engine)
350
+ subselect.project Arel.sql(update.key.name)
351
+ subselect.from subsubselect.as('__active_record_temp')
352
+
353
+ update.where update.key.in(subselect)
354
+ else
355
+ update.table select.source
356
+ update.wheres = select.constraints
357
+ end
358
+ end
359
+
360
+ def show_variable(var)
361
+ res = execute("show variables like '#{var}'")
362
+ result_row = res.detect {|row| row["Variable_name"] == var }
363
+ result_row && result_row["Value"]
364
+ end
365
+
366
+ def charset
367
+ show_variable("character_set_database")
368
+ end
369
+
370
+ def collation
371
+ show_variable("collation_database")
372
+ end
373
+
374
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
375
+ return super unless type.to_s == 'integer'
376
+
377
+ case limit
378
+ when 1; 'tinyint'
379
+ when 2; 'smallint'
380
+ when 3; 'mediumint'
381
+ when nil, 4, 11; 'int(11)' # compatibility with MySQL default
382
+ when 5..8; 'bigint'
383
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}")
384
+ end
385
+ end
386
+
387
+ def add_column_position!(sql, options)
388
+ if options[:first]
389
+ sql << " FIRST"
390
+ elsif options[:after]
391
+ sql << " AFTER #{quote_column_name(options[:after])}"
392
+ end
393
+ end
394
+
395
+ protected
396
+ def quoted_columns_for_index(column_names, options = {})
397
+ length = options[:length] if options.is_a?(Hash)
398
+
399
+ case length
400
+ when Hash
401
+ column_names.map { |name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
402
+ when Fixnum
403
+ column_names.map { |name| "#{quote_column_name(name)}(#{length})" }
404
+ else
405
+ column_names.map { |name| quote_column_name(name) }
406
+ end
407
+ end
408
+
409
+ def translate_exception(exception, message)
410
+ return super unless exception.respond_to?(:errno)
411
+
412
+ case exception.errno
413
+ when 1062
414
+ ::ActiveRecord::RecordNotUnique.new(message, exception)
415
+ when 1452
416
+ ::ActiveRecord::InvalidForeignKey.new(message, exception)
417
+ else
418
+ super
419
+ end
420
+ end
421
+
422
+ private
423
+ def column_for(table_name, column_name)
424
+ unless column = columns(table_name).find { |c| c.name == column_name.to_s }
425
+ raise "No such column: #{table_name}.#{column_name}"
426
+ end
427
+ column
428
+ end
429
+
430
+ def show_create_table(table)
431
+ select_one("SHOW CREATE TABLE #{quote_table_name(table)}")
432
+ end
433
+
434
+ def supports_views?
435
+ false
436
+ end
437
+ end
438
+ end
439
+
440
+ module ActiveRecord
441
+ module ConnectionAdapters
442
+ # Remove any vestiges of core/Ruby MySQL adapter
443
+ remove_const(:MysqlColumn) if const_defined?(:MysqlColumn)
444
+ remove_const(:MysqlAdapter) if const_defined?(:MysqlAdapter)
445
+
446
+ class MysqlColumn < JdbcColumn
447
+ include ArJdbc::MySQL::ColumnExtensions
448
+
449
+ def initialize(name, *args)
450
+ if Hash === name
451
+ super
452
+ else
453
+ super(nil, name, *args)
454
+ end
455
+ end
456
+
457
+ def call_discovered_column_callbacks(*)
458
+ end
459
+ end
460
+
461
+ class MysqlAdapter < JdbcAdapter
462
+ include ArJdbc::MySQL
463
+
464
+ def initialize(*args)
465
+ super
466
+ configure_connection
467
+ end
468
+
469
+ def jdbc_connection_class(spec)
470
+ ::ArJdbc::MySQL.jdbc_connection_class
471
+ end
472
+
473
+ def jdbc_column_class
474
+ ActiveRecord::ConnectionAdapters::MysqlColumn
475
+ end
476
+
477
+ alias_chained_method :columns, :query_cache, :jdbc_columns
478
+
479
+ protected
480
+ def exec_insert(sql, name, binds)
481
+ binds = binds.dup
482
+
483
+ # Pretend to support bind parameters
484
+ unless binds.empty?
485
+ sql = sql.gsub('?') { quote(*binds.shift.reverse) }
486
+ end
487
+ execute sql, name
488
+ end
489
+ alias :exec_update :exec_insert
490
+ alias :exec_delete :exec_insert
491
+
492
+ end
493
+ end
494
+ end
495
+
496
+ module Mysql # :nodoc:
497
+ remove_const(:Error) if const_defined?(:Error)
498
+
499
+ class Error < ::ActiveRecord::JDBCError
500
+ end
501
+
502
+ def self.client_version
503
+ 50400 # faked out for AR tests
504
+ end
505
+ end