activerecord-jdbc-adapter 1.2.5 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +5 -1
  3. data/Appraisals +5 -5
  4. data/Gemfile +9 -1
  5. data/Gemfile.lock +44 -10
  6. data/History.txt +126 -2
  7. data/README.md +246 -0
  8. data/Rakefile +34 -25
  9. data/activerecord-jdbc-adapter.gemspec +1 -1
  10. data/gemfiles/rails23.gemfile +5 -3
  11. data/gemfiles/rails23.gemfile.lock +26 -18
  12. data/gemfiles/rails30.gemfile +4 -2
  13. data/gemfiles/rails30.gemfile.lock +16 -8
  14. data/gemfiles/rails31.gemfile +4 -2
  15. data/gemfiles/rails31.gemfile.lock +16 -9
  16. data/gemfiles/rails32.gemfile +4 -2
  17. data/gemfiles/rails32.gemfile.lock +15 -8
  18. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  19. data/lib/arel/visitors/sql_server.rb +3 -0
  20. data/lib/arjdbc.rb +3 -5
  21. data/lib/arjdbc/db2.rb +1 -0
  22. data/lib/arjdbc/db2/adapter.rb +302 -196
  23. data/lib/arjdbc/db2/connection_methods.rb +18 -0
  24. data/lib/arjdbc/derby/active_record_patch.rb +12 -0
  25. data/lib/arjdbc/derby/adapter.rb +180 -158
  26. data/lib/arjdbc/derby/connection_methods.rb +5 -1
  27. data/lib/arjdbc/firebird/adapter.rb +27 -19
  28. data/lib/arjdbc/h2/adapter.rb +162 -7
  29. data/lib/arjdbc/h2/connection_methods.rb +5 -1
  30. data/lib/arjdbc/hsqldb.rb +1 -1
  31. data/lib/arjdbc/hsqldb/adapter.rb +96 -61
  32. data/lib/arjdbc/hsqldb/connection_methods.rb +5 -1
  33. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  34. data/lib/arjdbc/informix/adapter.rb +56 -55
  35. data/lib/arjdbc/jdbc/adapter.rb +173 -86
  36. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  37. data/lib/arjdbc/jdbc/column.rb +28 -23
  38. data/lib/arjdbc/jdbc/connection.rb +10 -6
  39. data/lib/arjdbc/jdbc/driver.rb +13 -5
  40. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +21 -0
  41. data/lib/arjdbc/mssql.rb +1 -1
  42. data/lib/arjdbc/mssql/adapter.rb +51 -53
  43. data/lib/arjdbc/mssql/connection_methods.rb +8 -1
  44. data/lib/arjdbc/mysql.rb +1 -1
  45. data/lib/arjdbc/mysql/adapter.rb +186 -150
  46. data/lib/arjdbc/mysql/connection_methods.rb +9 -9
  47. data/lib/arjdbc/mysql/explain_support.rb +85 -0
  48. data/lib/arjdbc/oracle.rb +1 -1
  49. data/lib/arjdbc/oracle/adapter.rb +232 -125
  50. data/lib/arjdbc/oracle/connection_methods.rb +2 -2
  51. data/lib/arjdbc/postgresql.rb +1 -1
  52. data/lib/arjdbc/postgresql/adapter.rb +134 -86
  53. data/lib/arjdbc/postgresql/connection_methods.rb +6 -4
  54. data/lib/arjdbc/postgresql/explain_support.rb +55 -0
  55. data/lib/arjdbc/sqlite3.rb +1 -1
  56. data/lib/arjdbc/sqlite3/adapter.rb +176 -108
  57. data/lib/arjdbc/sqlite3/connection_methods.rb +5 -5
  58. data/lib/arjdbc/sqlite3/explain_support.rb +32 -0
  59. data/lib/arjdbc/sybase/adapter.rb +7 -6
  60. data/lib/arjdbc/version.rb +1 -1
  61. data/pom.xml +1 -1
  62. data/rakelib/02-test.rake +9 -11
  63. data/rakelib/rails.rake +18 -10
  64. data/src/java/arjdbc/db2/DB2Module.java +70 -0
  65. data/src/java/arjdbc/derby/DerbyModule.java +24 -5
  66. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +66 -0
  67. data/src/java/arjdbc/jdbc/AdapterJavaService.java +14 -7
  68. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +111 -89
  69. data/src/java/arjdbc/mysql/MySQLModule.java +79 -70
  70. data/src/java/arjdbc/oracle/OracleModule.java +74 -0
  71. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +5 -10
  72. data/src/java/arjdbc/sqlite3/SQLite3Module.java +77 -0
  73. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +127 -0
  74. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +25 -111
  75. data/src/java/arjdbc/util/QuotingUtils.java +104 -0
  76. data/test/abstract_db_create.rb +6 -6
  77. data/test/activerecord/connection_adapters/type_conversion_test.rb +2 -2
  78. data/test/assets/flowers.jpg +0 -0
  79. data/test/binary.rb +67 -0
  80. data/test/db/db2.rb +30 -7
  81. data/test/db/jdbc.rb +4 -2
  82. data/test/db/oracle.rb +18 -27
  83. data/test/db2_binary_test.rb +6 -0
  84. data/test/db2_serialize_test.rb +6 -0
  85. data/test/db2_simple_test.rb +20 -25
  86. data/test/db2_test.rb +71 -0
  87. data/test/derby_binary_test.rb +6 -0
  88. data/test/derby_migration_test.rb +42 -35
  89. data/test/derby_reset_column_information_test.rb +1 -0
  90. data/test/derby_row_locking_test.rb +17 -0
  91. data/test/derby_schema_dump_test.rb +9 -0
  92. data/test/derby_serialize_test.rb +6 -0
  93. data/test/derby_simple_test.rb +59 -17
  94. data/test/generic_jdbc_connection_test.rb +112 -5
  95. data/test/h2_binary_test.rb +6 -0
  96. data/test/h2_change_column_test.rb +1 -1
  97. data/test/h2_schema_dump_test.rb +25 -0
  98. data/test/h2_serialize_test.rb +6 -0
  99. data/test/h2_simple_test.rb +23 -9
  100. data/test/has_many_through.rb +18 -4
  101. data/test/hsqldb_binary_test.rb +6 -0
  102. data/test/hsqldb_schema_dump_test.rb +15 -0
  103. data/test/hsqldb_serialize_test.rb +6 -0
  104. data/test/hsqldb_simple_test.rb +1 -0
  105. data/test/informix_simple_test.rb +1 -1
  106. data/test/jdbc/db2.rb +23 -0
  107. data/test/jdbc/oracle.rb +23 -0
  108. data/test/jdbc_common.rb +3 -110
  109. data/test/jndi_callbacks_test.rb +0 -2
  110. data/test/jndi_test.rb +2 -0
  111. data/test/models/binary.rb +18 -0
  112. data/test/models/custom_pk_name.rb +1 -0
  113. data/test/models/data_types.rb +11 -2
  114. data/test/models/entry.rb +1 -1
  115. data/test/models/string_id.rb +2 -2
  116. data/test/models/thing.rb +1 -1
  117. data/test/models/topic.rb +32 -0
  118. data/test/mssql_legacy_types_test.rb +1 -1
  119. data/test/mssql_limit_offset_test.rb +13 -3
  120. data/test/mssql_serialize_test.rb +6 -0
  121. data/test/mysql_binary_test.rb +6 -0
  122. data/test/mysql_schema_dump_test.rb +220 -0
  123. data/test/mysql_serialize_test.rb +6 -0
  124. data/test/mysql_simple_test.rb +22 -2
  125. data/test/mysql_test.rb +93 -0
  126. data/test/oracle_binary_test.rb +6 -0
  127. data/test/oracle_limit_test.rb +2 -1
  128. data/test/oracle_serialize_test.rb +6 -0
  129. data/test/oracle_simple_test.rb +61 -0
  130. data/test/oracle_specific_test.rb +77 -26
  131. data/test/postgres_binary_test.rb +6 -0
  132. data/test/postgres_native_type_mapping_test.rb +12 -11
  133. data/test/postgres_nonseq_pkey_test.rb +1 -0
  134. data/test/postgres_reserved_test.rb +1 -0
  135. data/test/postgres_reset_column_information_test.rb +1 -0
  136. data/test/postgres_row_locking_test.rb +21 -0
  137. data/test/postgres_schema_dump_test.rb +88 -0
  138. data/test/postgres_schema_search_path_test.rb +1 -0
  139. data/test/postgres_simple_test.rb +62 -89
  140. data/test/postgres_table_alias_length_test.rb +1 -0
  141. data/test/postgres_test.rb +31 -0
  142. data/test/postgres_type_conversion_test.rb +16 -16
  143. data/test/row_locking.rb +69 -64
  144. data/test/schema_dump.rb +168 -0
  145. data/test/serialize.rb +277 -0
  146. data/test/simple.rb +326 -122
  147. data/test/sqlite3_serialize_test.rb +6 -0
  148. data/test/sqlite3_simple_test.rb +51 -84
  149. data/test/sqlite3_type_conversion_test.rb +101 -0
  150. data/test/test_helper.rb +224 -0
  151. metadata +325 -366
  152. data/README.rdoc +0 -214
  153. data/test/db/logger.rb +0 -3
  154. data/test/derby_multibyte_test.rb +0 -11
  155. data/test/mysql_info_test.rb +0 -123
@@ -0,0 +1,18 @@
1
+ class ActiveRecord::Base
2
+ class << self
3
+ def db2_connection(config)
4
+ config[:url] ||= begin
5
+ if config[:host] # Type 4 URL: jdbc:db2://server:port/database
6
+ config[:port] ||= 50000
7
+ "jdbc:db2://#{config[:host]}:#{config[:port]}/#{config[:database]}"
8
+ else # Type 2 URL: jdbc:db2:database
9
+ "jdbc:db2:#{config[:database]}"
10
+ end
11
+ end
12
+ config[:driver] ||= ::ArJdbc::DB2::DRIVER_NAME
13
+ config[:adapter_spec] = ::ArJdbc::DB2
14
+ jdbc_connection(config)
15
+ end
16
+ alias_method :jdbcdb2_connection, :db2_connection
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ # Needed because Rails is broken wrt to quoting of some values.
2
+ # Most databases are nice about it, but not Derby.
3
+ # The real issue is that you can't compare a CHAR value to a NUMBER column.
4
+ ActiveRecord::Associations::ClassMethods.module_eval do
5
+ private
6
+ def select_limited_ids_list(options, join_dependency)
7
+ connection.select_all(
8
+ construct_finder_sql_for_association_limiting(options, join_dependency),
9
+ "#{name} Load IDs For Limited Eager Loading"
10
+ ).collect { |row| connection.quote(row[primary_key], columns_hash[primary_key]) }.join(", ")
11
+ end
12
+ end
@@ -1,51 +1,57 @@
1
1
  require 'arjdbc/jdbc/missing_functionality_helper'
2
2
 
3
- module ::ArJdbc
3
+ module ArJdbc
4
4
  module Derby
5
- def self.column_selector
6
- [/derby/i, lambda {|cfg,col| col.extend(::ArJdbc::Derby::Column)}]
7
- end
8
-
9
- def self.monkey_rails
10
- unless defined?(@already_monkeyd)
11
- # Needed because Rails is broken wrt to quoting of
12
- # some values. Most databases are nice about it,
13
- # but not Derby. The real issue is that you can't
14
- # compare a CHAR value to a NUMBER column.
15
- ::ActiveRecord::Associations::ClassMethods.module_eval do
16
- private
17
-
18
- def select_limited_ids_list(options, join_dependency)
19
- connection.select_all(
20
- construct_finder_sql_for_association_limiting(options, join_dependency),
21
- "#{name} Load IDs For Limited Eager Loading"
22
- ).collect { |row| connection.quote(row[primary_key], columns_hash[primary_key]) }.join(", ")
23
- end
24
- end
25
-
26
- @already_monkeyd = true
27
- end
28
- end
29
5
 
30
6
  def self.extended(adapter)
31
- monkey_rails
7
+ require 'arjdbc/derby/active_record_patch'
32
8
  adapter.configure_connection
33
9
  end
34
10
 
35
11
  def self.included(*args)
36
- monkey_rails
12
+ require 'arjdbc/derby/active_record_patch'
37
13
  end
38
14
 
15
+ def self.column_selector
16
+ [ /derby/i, lambda { |cfg, column| column.extend(::ArJdbc::Derby::Column) } ]
17
+ end
18
+
39
19
  def configure_connection
40
- execute("SET ISOLATION = SERIALIZABLE") # This must be done or SELECT...FOR UPDATE won't work how we expect
20
+ execute("SET ISOLATION = SERIALIZABLE")
21
+ # must be done or SELECT...FOR UPDATE won't work how we expect
41
22
  end
42
23
 
43
24
  module Column
25
+
26
+ private
27
+
28
+ def extract_limit(sql_type)
29
+ case @sql_type = sql_type.downcase
30
+ when /^smallint/i then @sql_type = 'smallint'; limit = 2
31
+ when /^bigint/i then @sql_type = 'bigint'; limit = 8
32
+ when /^double/i then @sql_type = 'double'; limit = 8 # DOUBLE PRECISION
33
+ when /^real/i then @sql_type = 'real'; limit = 4
34
+ when /^integer/i then @sql_type = 'integer'; limit = 4
35
+ when /^datetime/i then @sql_type = 'datetime'; limit = nil
36
+ when /^timestamp/i then @sql_type = 'timestamp'; limit = nil
37
+ when /^time/i then @sql_type = 'time'; limit = nil
38
+ when /^date/i then @sql_type = 'date'; limit = nil
39
+ else
40
+ limit = super
41
+ # handle maximum length for a VARCHAR string :
42
+ limit = 32672 if ! limit && @sql_type.index('varchar') == 0
43
+ end
44
+ limit
45
+ end
46
+
44
47
  def simplified_type(field_type)
45
48
  case field_type
46
- when /smallint/i then :boolean
47
- when /real/i then :float
48
- when /^timestamp/i then :datetime
49
+ when /^smallint/i then :boolean
50
+ when /^bigint|int/i then :integer
51
+ when /^real|double/i then :float
52
+ when /^dec/i then # DEC is a DECIMAL alias
53
+ extract_scale(field_type) == 0 ? :integer : :decimal
54
+ when /^timestamp/i then :datetime
49
55
  else
50
56
  super
51
57
  end
@@ -53,20 +59,26 @@ module ::ArJdbc
53
59
 
54
60
  # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
55
61
  def default_value(value)
56
- # jdbc returns column default strings with actual single quotes around the value.
62
+ # JDBC returns column default strings with actual single quotes around the value.
57
63
  return $1 if value =~ /^'(.*)'$/
58
64
  return nil if value == "GENERATED_BY_DEFAULT"
59
65
  value
60
66
  end
67
+
61
68
  end
62
69
 
63
- def adapter_name #:nodoc:
64
- 'Derby'
70
+ ADAPTER_NAME = 'Derby'
71
+
72
+ def adapter_name # :nodoc:
73
+ ADAPTER_NAME
65
74
  end
66
75
 
67
76
  def self.arel2_visitors(config)
68
77
  require 'arel/visitors/derby'
69
- {}.tap {|v| %w(derby jdbcderby).each {|a| v[a] = ::Arel::Visitors::Derby } }
78
+ {
79
+ 'derby' => ::Arel::Visitors::Derby,
80
+ 'jdbcderby' => ::Arel::Visitors::Derby,
81
+ }
70
82
  end
71
83
 
72
84
  include ArJdbc::MissingFunctionalityHelper
@@ -75,28 +87,50 @@ module ::ArJdbc
75
87
  128
76
88
  end
77
89
 
78
- # Convert the specified column type to a SQL string.
79
- # In Derby, the following cannot specify a limit:
80
- # - integer
81
- # - boolean (smallint)
82
- # - datetime (timestamp)
83
- # - date
84
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
85
- return super unless [:integer, :boolean, :timestamp, :datetime, :date].include? type
86
-
87
- native = native_database_types[type.to_s.downcase.to_sym]
88
- native.is_a?(Hash) ? native[:name] : native
90
+ NATIVE_DATABASE_TYPES = {
91
+ :primary_key => "int GENERATED BY DEFAULT AS identity NOT NULL PRIMARY KEY",
92
+ :string => { :name => "varchar", :limit => 255 },
93
+ :text => { :name => "clob" }, # 2,147,483,647
94
+ :binary => { :name => "blob" }, # 2,147,483,647
95
+ :float => { :name => "float", :limit => 8 }, # DOUBLE PRECISION
96
+ :decimal => { :name => "decimal", :precision => 5, :scale => 0 }, # defaults
97
+ :numeric => { :name => "decimal", :precision => 5, :scale => 0 }, # defaults
98
+ :integer => { :name => "integer", :limit => 4 },
99
+ :smallint => { :name => "smallint", :limit => 2 },
100
+ :bigint => { :name => "bigint", :limit => 8 },
101
+ :real => { :name => "real", :limit => 4 },
102
+ :double => { :name => "double", :limit => 8 },
103
+ :date => { :name => "date" },
104
+ :time => { :name => "time" },
105
+ :datetime => { :name => "timestamp" },
106
+ :timestamp => { :name => "timestamp" },
107
+ :boolean => { :name => "smallint" },
108
+ }
109
+
110
+ def native_database_types
111
+ super.merge NATIVE_DATABASE_TYPES
112
+ end
113
+
114
+ def modify_types(types)
115
+ super(types)
116
+ types[:primary_key] = NATIVE_DATABASE_TYPES[:primary_key]
117
+ [ :string, :float, :decimal, :numeric, :integer,
118
+ :smallint, :bigint, :real, :double ].each do |type|
119
+ types[type] = NATIVE_DATABASE_TYPES[type].dup
120
+ end
121
+ types[:boolean] = NATIVE_DATABASE_TYPES[:boolean].dup
122
+ types
89
123
  end
124
+
125
+ # in Derby, the following cannot specify a limit :
126
+ NO_LIMIT_TYPES = [ :integer, :boolean, :timestamp, :datetime, :date, :time ] # :nodoc:
127
+
128
+ # Convert the specified column type to a SQL string.
129
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) # :nodoc:
130
+ return super unless NO_LIMIT_TYPES.include?(t = type.to_s.downcase.to_sym)
90
131
 
91
- def modify_types(tp)
92
- tp[:primary_key] = "int generated by default as identity NOT NULL PRIMARY KEY"
93
- tp[:string][:limit] = 256
94
- tp[:integer][:limit] = nil
95
- tp[:boolean] = {:name => "smallint"}
96
- tp[:datetime] = {:name => "timestamp", :limit => nil}
97
- tp[:timestamp][:limit] = nil
98
- tp[:date][:limit] = nil
99
- tp
132
+ native_type = NATIVE_DATABASE_TYPES[t]
133
+ native_type.is_a?(Hash) ? native_type[:name] : native_type
100
134
  end
101
135
 
102
136
  # Override default -- fix case where ActiveRecord passes :default => nil, :null => true
@@ -106,10 +140,6 @@ module ::ArJdbc
106
140
  super
107
141
  end
108
142
 
109
- def classes_for_table_name(table)
110
- ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
111
- end
112
-
113
143
  # Set the sequence to the max value of the table's column.
114
144
  def reset_sequence!(table, column, sequence = nil)
115
145
  mpk = select_value("SELECT MAX(#{quote_column_name(column)}) FROM #{quote_table_name(table)}")
@@ -125,6 +155,11 @@ module ::ArJdbc
125
155
  end
126
156
  end
127
157
 
158
+ def classes_for_table_name(table)
159
+ ActiveRecord::Base.send(:subclasses).select { |klass| klass.table_name == table }
160
+ end
161
+ private :classes_for_table_name
162
+
128
163
  def remove_index(table_name, options) #:nodoc:
129
164
  execute "DROP INDEX #{index_name(table_name, options)}"
130
165
  end
@@ -133,44 +168,6 @@ module ::ArJdbc
133
168
  execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
134
169
  end
135
170
 
136
- AUTO_INC_STMT2 = "SELECT AUTOINCREMENTSTART, AUTOINCREMENTINC, COLUMNNAME, REFERENCEID, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = (SELECT T.TABLEID FROM SYS.SYSTABLES T WHERE T.TABLENAME = '%s') AND COLUMNNAME = '%s'"
137
-
138
- def add_quotes(name)
139
- return name unless name
140
- %Q{"#{name}"}
141
- end
142
-
143
- def strip_quotes(str)
144
- return str unless str
145
- return str unless /^(["']).*\1$/ =~ str
146
- str[1..-2]
147
- end
148
-
149
- def expand_double_quotes(name)
150
- return name unless name && name['"']
151
- name.gsub(/"/,'""')
152
- end
153
-
154
- def auto_increment_stmt(tname, cname)
155
- stmt = AUTO_INC_STMT2 % [tname, strip_quotes(cname)]
156
- data = execute(stmt).first
157
- if data
158
- start = data['autoincrementstart']
159
- if start
160
- coldef = ""
161
- coldef << " GENERATED " << (data['columndefault'].nil? ? "ALWAYS" : "BY DEFAULT ")
162
- coldef << "AS IDENTITY (START WITH "
163
- coldef << start
164
- coldef << ", INCREMENT BY "
165
- coldef << data['autoincrementinc']
166
- coldef << ")"
167
- return coldef
168
- end
169
- end
170
- ""
171
- end
172
-
173
-
174
171
  def add_column(table_name, column_name, type, options = {})
175
172
  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])}"
176
173
  add_column_options!(add_column_sql, options)
@@ -178,16 +175,18 @@ module ::ArJdbc
178
175
  end
179
176
 
180
177
  def execute(sql, name = nil, binds = [])
181
- sql = extract_sql(sql)
178
+ sql = to_sql(sql)
182
179
  if sql =~ /\A\s*(UPDATE|INSERT)/i
183
- i = sql =~ /\swhere\s/im
184
- if i
185
- sql[i..-1] = sql[i..-1].gsub(/!=\s*NULL/, 'IS NOT NULL').gsub(/=\sNULL/i, 'IS NULL')
180
+ if ( i = sql =~ /\sWHERE\s/im )
181
+ where_part = sql[i..-1]; sql = sql.dup
182
+ where_part.gsub!(/!=\s*NULL/, 'IS NOT NULL')
183
+ where_part.gsub!(/=\sNULL/i, 'IS NULL')
184
+ sql[i..-1] = where_part
186
185
  end
187
186
  else
188
- sql.gsub!(/= NULL/i, 'IS NULL')
187
+ sql = sql.gsub(/=\sNULL/i, 'IS NULL')
189
188
  end
190
- super
189
+ super(sql, name, binds)
191
190
  end
192
191
 
193
192
  # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
@@ -212,42 +211,38 @@ module ::ArJdbc
212
211
  sql
213
212
  end
214
213
 
215
- SIZEABLE = %w(VARCHAR CLOB BLOB)
214
+ SIZEABLE = %w(VARCHAR CLOB BLOB) # :nodoc:
216
215
 
217
- def structure_dump #:nodoc:
218
- definition=""
219
- rs = @connection.connection.meta_data.getTables(nil,nil,nil,["TABLE"].to_java(:string))
220
- while rs.next
221
- tname = rs.getString(3)
222
- definition << "CREATE TABLE #{tname} (\n"
223
- rs2 = @connection.connection.meta_data.getColumns(nil,nil,tname,nil)
216
+ def structure_dump # :nodoc:
217
+ definition = ""
218
+ meta_data = @connection.connection.meta_data
219
+ tables_rs = meta_data.getTables(nil, nil, nil, ["TABLE"].to_java(:string))
220
+ while tables_rs.next
221
+ table_name = tables_rs.getString(3)
222
+ definition << "CREATE TABLE #{table_name} (\n"
223
+ columns_rs = meta_data.getColumns(nil, nil, table_name, nil)
224
224
  first_col = true
225
- while rs2.next
226
- col_name = add_quotes(rs2.getString(4));
227
- default = ""
228
- d1 = rs2.getString(13)
225
+ while columns_rs.next
226
+ column_name = add_quotes(columns_rs.getString(4));
227
+ default = ''
228
+ d1 = columns_rs.getString(13)
229
229
  if d1 =~ /^GENERATED_/
230
- default = auto_increment_stmt(tname, col_name)
230
+ default = auto_increment_stmt(table_name, column_name)
231
231
  elsif d1
232
232
  default = " DEFAULT #{d1}"
233
233
  end
234
234
 
235
- type = rs2.getString(6)
236
- col_size = rs2.getString(7)
237
- nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
238
- create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
239
- " " +
240
- type +
241
- (SIZEABLE.include?(type) ? "(#{col_size})" : "") +
242
- nulling +
243
- default
244
- if !first_col
245
- create_col_string = ",\n #{create_col_string}"
246
- else
247
- create_col_string = " #{create_col_string}"
248
- end
249
-
250
- definition << create_col_string
235
+ type = columns_rs.getString(6)
236
+ column_size = columns_rs.getString(7)
237
+ nulling = (columns_rs.getString(18) == 'NO' ? " NOT NULL" : "")
238
+ create_column = add_quotes(expand_double_quotes(strip_quotes(column_name)))
239
+ create_column << " #{type}"
240
+ create_column << ( SIZEABLE.include?(type) ? "(#{column_size})" : "" )
241
+ create_column << nulling
242
+ create_column << default
243
+
244
+ create_column = first_col ? " #{create_column}" : ",\n #{create_column}"
245
+ definition << create_column
251
246
 
252
247
  first_col = false
253
248
  end
@@ -255,10 +250,35 @@ module ::ArJdbc
255
250
  end
256
251
  definition
257
252
  end
258
-
259
- def remove_column(table_name, column_name)
260
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)} RESTRICT"
253
+
254
+ def remove_column(table_name, *column_names) # :nodoc:
255
+ for column_name in column_names.flatten
256
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)} RESTRICT"
257
+ end
261
258
  end
259
+
260
+ AUTO_INC_STMT2 = "" +
261
+ "SELECT AUTOINCREMENTSTART, AUTOINCREMENTINC, COLUMNNAME, REFERENCEID, COLUMNDEFAULT " +
262
+ "FROM SYS.SYSCOLUMNS WHERE REFERENCEID = " +
263
+ "(SELECT T.TABLEID FROM SYS.SYSTABLES T WHERE T.TABLENAME = '%s') AND COLUMNNAME = '%s'"
264
+
265
+ def auto_increment_stmt(tname, cname)
266
+ stmt = AUTO_INC_STMT2 % [ tname, strip_quotes(cname) ]
267
+ if data = execute(stmt).first
268
+ if start = data['autoincrementstart']
269
+ coldef = ""
270
+ coldef << " GENERATED " << (data['columndefault'].nil? ? "ALWAYS" : "BY DEFAULT ")
271
+ coldef << "AS IDENTITY (START WITH "
272
+ coldef << start
273
+ coldef << ", INCREMENT BY "
274
+ coldef << data['autoincrementinc']
275
+ coldef << ")"
276
+ return coldef
277
+ end
278
+ end
279
+ ""
280
+ end
281
+ private :auto_increment_stmt
262
282
 
263
283
  # Notes about changing in Derby:
264
284
  # http://db.apache.org/derby/docs/10.2/ref/rrefsqlj81859.html#rrefsqlj81859__rrefsqlj37860)
@@ -266,8 +286,8 @@ module ::ArJdbc
266
286
  # We support changing columns using the strategy outlined in:
267
287
  # https://issues.apache.org/jira/browse/DERBY-1515
268
288
  #
269
- # This feature has not made it into a formal release and is not in Java 6. We will
270
- # need to conditionally support this somehow (supposed to arrive for 10.3.0.0)
289
+ # This feature has not made it into a formal release and is not in Java 6.
290
+ # We will need to conditionally support this (supposed to arrive for 10.3.0.0).
271
291
  def change_column(table_name, column_name, type, options = {})
272
292
  # null/not nulling is easy, handle that separately
273
293
  if options.include?(:null)
@@ -307,7 +327,7 @@ module ::ArJdbc
307
327
  @connection.primary_keys table_name.to_s.upcase
308
328
  end
309
329
 
310
- def columns(table_name, name=nil)
330
+ def columns(table_name, name = nil)
311
331
  @connection.columns_internal(table_name.to_s, name, derby_schema)
312
332
  end
313
333
 
@@ -316,43 +336,45 @@ module ::ArJdbc
316
336
  end
317
337
 
318
338
  def recreate_database(db_name, options = {})
319
- tables.each do |t|
320
- drop_table t
321
- end
339
+ tables.each { |table| drop_table table }
322
340
  end
323
341
 
324
- def quote_column_name(name) #:nodoc:
342
+ def quote_column_name(name) # :nodoc:
325
343
  %Q{"#{name.to_s.upcase.gsub(/\"/, '""')}"}
326
344
  end
327
345
 
328
- def quoted_true
329
- '1'
346
+ def add_limit_offset!(sql, options) # :nodoc:
347
+ sql << " OFFSET #{options[:offset]} ROWS" if options[:offset]
348
+ # ROWS/ROW and FIRST/NEXT mean the same
349
+ sql << " FETCH FIRST #{options[:limit]} ROWS ONLY" if options[:limit]
330
350
  end
331
351
 
332
- def quoted_false
333
- '0'
352
+ private
353
+
354
+ def add_quotes(name)
355
+ return name unless name
356
+ %Q{"#{name}"}
334
357
  end
335
358
 
336
- def add_limit_offset!(sql, options) #:nodoc:
337
- if options[:offset]
338
- sql << " OFFSET #{options[:offset]} ROWS"
339
- end
340
- if options[:limit]
341
- #ROWS/ROW and FIRST/NEXT mean the same
342
- sql << " FETCH FIRST #{options[:limit]} ROWS ONLY"
343
- end
359
+ def strip_quotes(str)
360
+ return str unless str
361
+ return str unless /^(["']).*\1$/ =~ str
362
+ str[1..-2]
344
363
  end
345
364
 
346
- private
365
+ def expand_double_quotes(name)
366
+ return name unless name && name['"']
367
+ name.gsub('"', '""')
368
+ end
369
+
347
370
  # Derby appears to define schemas using the username
348
371
  def derby_schema
349
372
  if @config.has_key?(:schema)
350
- config[:schema]
373
+ @config[:schema]
351
374
  else
352
375
  (@config[:username] && @config[:username].to_s) || ''
353
376
  end
354
377
  end
378
+
355
379
  end
356
380
  end
357
-
358
-