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
@@ -7,6 +7,6 @@ class ActiveRecord::Base
7
7
  config[:adapter_spec] = ::ArJdbc::Oracle
8
8
  jdbc_connection(config)
9
9
  end
10
+ alias_method :jdbcoracle_connection, :oracle_connection
10
11
  end
11
- end
12
-
12
+ end
@@ -1,3 +1,3 @@
1
1
  require 'arjdbc/jdbc'
2
- require 'arjdbc/postgresql/connection_methods'
3
2
  require 'arjdbc/postgresql/adapter'
3
+ require 'arjdbc/postgresql/connection_methods'
@@ -1,6 +1,4 @@
1
- module ActiveRecord::ConnectionAdapters
2
- PostgreSQLAdapter = Class.new(AbstractAdapter) unless const_defined?(:PostgreSQLAdapter)
3
- end
1
+ require 'arjdbc/postgresql/explain_support'
4
2
 
5
3
  module ::ArJdbc
6
4
  module PostgreSQL
@@ -167,29 +165,16 @@ module ::ArJdbc
167
165
  # constants taken from postgresql_adapter in rails project
168
166
  ADAPTER_NAME = 'PostgreSQL'
169
167
 
170
- NATIVE_DATABASE_TYPES = {
171
- :primary_key => "serial primary key",
172
- :string => { :name => "character varying", :limit => 255 },
173
- :text => { :name => "text" },
174
- :integer => { :name => "integer" },
175
- :float => { :name => "float" },
176
- :decimal => { :name => "decimal" },
177
- :datetime => { :name => "timestamp" },
178
- :timestamp => { :name => "timestamp" },
179
- :time => { :name => "time" },
180
- :date => { :name => "date" },
181
- :binary => { :name => "bytea" },
182
- :boolean => { :name => "boolean" },
183
- :xml => { :name => "xml" },
184
- :tsvector => { :name => "tsvector" }
185
- }
186
-
187
168
  def adapter_name #:nodoc:
188
169
  ADAPTER_NAME
189
170
  end
190
171
 
191
172
  def self.arel2_visitors(config)
192
- {}.tap {|v| %w(postgresql pg jdbcpostgresql).each {|a| v[a] = ::Arel::Visitors::PostgreSQL } }
173
+ {
174
+ 'postgresql' => ::Arel::Visitors::PostgreSQL,
175
+ 'jdbcpostgresql' => ::Arel::Visitors::PostgreSQL,
176
+ 'pg' => ::Arel::Visitors::PostgreSQL
177
+ }
193
178
  end
194
179
 
195
180
  def postgresql_version
@@ -204,66 +189,94 @@ module ::ArJdbc
204
189
  end
205
190
  end
206
191
 
192
+ NATIVE_DATABASE_TYPES = {
193
+ :primary_key => "serial primary key",
194
+ :string => { :name => "character varying", :limit => 255 },
195
+ :text => { :name => "text" },
196
+ :integer => { :name => "integer" },
197
+ :float => { :name => "float" },
198
+ :decimal => { :name => "decimal" },
199
+ :datetime => { :name => "timestamp" },
200
+ :timestamp => { :name => "timestamp" },
201
+ :time => { :name => "time" },
202
+ :date => { :name => "date" },
203
+ :binary => { :name => "bytea" },
204
+ :boolean => { :name => "boolean" },
205
+ :xml => { :name => "xml" },
206
+ :tsvector => { :name => "tsvector" }
207
+ }
208
+
207
209
  def native_database_types
208
210
  NATIVE_DATABASE_TYPES
209
211
  end
210
-
211
- # Does PostgreSQL support migrations?
212
- def supports_migrations?
213
- true
214
- end
215
-
212
+
216
213
  # Enable standard-conforming strings if available.
217
214
  def standard_conforming_strings=(enable)
218
- old, self.client_min_messages = client_min_messages, 'panic'
219
- value = if(enable) then "on" else "off" end
220
- execute("SET standard_conforming_strings = #{value}", 'SCHEMA')
221
- @standard_conforming_strings = (value == "on")
222
- rescue
223
- @standard_conforming_strings = :unsupported
224
- ensure
225
- self.client_min_messages = old
215
+ client_min_messages = self.client_min_messages
216
+ begin
217
+ self.client_min_messages = 'panic'
218
+ value = enable ? "on" : "off"
219
+ execute("SET standard_conforming_strings = #{value}", 'SCHEMA')
220
+ @standard_conforming_strings = ( value == "on" )
221
+ rescue
222
+ @standard_conforming_strings = :unsupported
223
+ ensure
224
+ self.client_min_messages = client_min_messages
225
+ end
226
226
  end
227
227
 
228
- def standard_conforming_strings? #:nodoc:
228
+ def standard_conforming_strings? # :nodoc:
229
229
  if @standard_conforming_strings.nil?
230
+ client_min_messages = self.client_min_messages
230
231
  begin
231
- old, self.client_min_messages = client_min_messages, 'panic'
232
+ self.client_min_messages = 'panic'
232
233
  value = select_one('SHOW standard_conforming_strings', 'SCHEMA')['standard_conforming_strings']
233
- @standard_conforming_strings = (value == "on")
234
+ @standard_conforming_strings = ( value == "on" )
234
235
  rescue
235
236
  @standard_conforming_strings = :unsupported
236
237
  ensure
237
- self.client_min_messages = old
238
+ self.client_min_messages = client_min_messages
238
239
  end
239
240
  end
241
+ @standard_conforming_strings == true # return false if :unsupported
242
+ end
240
243
 
241
- # Return false if unsupported.
242
- @standard_conforming_strings == true
244
+ # Does PostgreSQL support migrations?
245
+ def supports_migrations? # :nodoc:
246
+ true
243
247
  end
244
248
 
249
+ # Does PostgreSQL support finding primary key on non-Active Record tables?
250
+ def supports_primary_key? # :nodoc:
251
+ true
252
+ end
253
+
245
254
  # Does PostgreSQL support standard conforming strings?
246
- def supports_standard_conforming_strings?
255
+ def supports_standard_conforming_strings? # :nodoc:
247
256
  standard_conforming_strings?
248
257
  @standard_conforming_strings != :unsupported
249
258
  end
250
259
 
251
- def supports_hex_escaped_bytea?
260
+ def supports_hex_escaped_bytea? # :nodoc:
252
261
  postgresql_version >= 90000
253
262
  end
254
263
 
255
- def supports_insert_with_returning?
264
+ def supports_insert_with_returning? # :nodoc:
256
265
  postgresql_version >= 80200
257
266
  end
258
267
 
259
- def supports_ddl_transactions?
268
+ def supports_ddl_transactions? # :nodoc:
260
269
  true
261
270
  end
262
271
 
263
- def supports_savepoints?
272
+ def supports_index_sort_order? # :nodoc:
264
273
  true
265
274
  end
266
-
275
+
276
+ def supports_savepoints? # :nodoc:
277
+ true
278
+ end
279
+
267
280
  def create_savepoint
268
281
  execute("SAVEPOINT #{current_savepoint_name}")
269
282
  end
@@ -364,10 +377,8 @@ module ::ArJdbc
364
377
  if supports_insert_with_returning? && id_value.nil?
365
378
  pk, sequence_name = *pk_and_sequence_for(table) unless pk
366
379
  if pk
367
- sql = substitute_binds(sql, binds)
368
- id_value = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
369
- clear_query_cache #FIXME: Why now?
370
- return id_value
380
+ sql = to_sql(sql, binds)
381
+ return select_value("#{sql} RETURNING #{quote_column_name(pk)}")
371
382
  end
372
383
  end
373
384
 
@@ -390,14 +401,8 @@ module ::ArJdbc
390
401
  end
391
402
  id_value
392
403
  end
393
-
394
- def primary_key(table)
395
- pk_and_sequence = pk_and_sequence_for(table)
396
- pk_and_sequence && pk_and_sequence.first
397
- end
398
-
399
- # taken from rails postgresql adapter
400
- # https://github.com/gfmurphy/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L611
404
+
405
+ # taken from rails postgresql_adapter.rb
401
406
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
402
407
  unless pk
403
408
  table_ref = extract_table_ref_from_insert_sql(sql)
@@ -408,6 +413,11 @@ module ::ArJdbc
408
413
 
409
414
  [sql, binds]
410
415
  end
416
+
417
+ def primary_key(table)
418
+ pk_and_sequence = pk_and_sequence_for(table)
419
+ pk_and_sequence && pk_and_sequence.first
420
+ end
411
421
 
412
422
  def pg_columns(table_name, name=nil)
413
423
  column_definitions(table_name).map do |row|
@@ -502,7 +512,7 @@ module ::ArJdbc
502
512
  def create_database(name, options = {})
503
513
  options = options.with_indifferent_access
504
514
  create_query = "CREATE DATABASE \"#{name}\" ENCODING='#{options[:encoding] || 'utf8'}'"
505
- create_query += options.symbolize_keys.sum do |key, value|
515
+ create_query += options.symbolize_keys.sum('') do |key, value|
506
516
  case key
507
517
  when :owner
508
518
  " OWNER = \"#{value}\""
@@ -548,7 +558,7 @@ module ::ArJdbc
548
558
  ENV['PGHOST'] = @config[:host] if @config[:host]
549
559
  ENV['PGPORT'] = @config[:port].to_s if @config[:port]
550
560
  ENV['PGPASSWORD'] = @config[:password].to_s if @config[:password]
551
- search_path = "--schema=#{schema_search_path}" if schema_search_path
561
+ search_path = "--schema=#{@config[:schema_search_path]}" if @config[:schema_search_path]
552
562
 
553
563
  @connection.connection.close
554
564
  begin
@@ -643,10 +653,16 @@ module ::ArJdbc
643
653
  "'#{value}'"
644
654
  when String
645
655
  case column.sql_type
646
- when 'bytea' then "E'#{escape_bytea(value)}'::bytea"
656
+ when 'bytea' then "E'#{escape_bytea(value)}'::bytea" # "'#{escape_bytea(value)}'"
647
657
  when 'xml' then "xml '#{quote_string(value)}'"
648
658
  when /^bit/
649
659
  case value
660
+ # NOTE: as reported with #60 this is not quite "right" :
661
+ # "0103" will be treated as hexadecimal string
662
+ # "0102" will be treated as hexadecimal string
663
+ # "0101" will be treated as binary string
664
+ # "0100" will be treated as binary string
665
+ # ... but is kept due Rails compatibility
650
666
  when /^[01]*$/ then "B'#{value}'" # Bit-string notation
651
667
  when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
652
668
  end
@@ -660,30 +676,29 @@ module ::ArJdbc
660
676
 
661
677
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
662
678
  # characters.
663
- def quote_string(s)
664
- quoted = s.gsub(/'/, "''")
665
- if !standard_conforming_strings?
679
+ def quote_string(string)
680
+ quoted = string.gsub("'", "''")
681
+ unless standard_conforming_strings?
666
682
  quoted.gsub!(/\\/, '\&\&')
667
683
  end
668
-
669
684
  quoted
670
685
  end
671
686
 
672
- def escape_bytea(s)
673
- if s
687
+ def escape_bytea(string)
688
+ if string
674
689
  if supports_hex_escaped_bytea?
675
- "\\\\x#{s.unpack("H*")[0]}"
690
+ "\\\\x#{string.unpack("H*")[0]}"
676
691
  else
677
692
  result = ''
678
- s.each_byte { |c| result << sprintf('\\\\%03o', c) }
693
+ string.each_byte { |c| result << sprintf('\\\\%03o', c) }
679
694
  result
680
695
  end
681
696
  end
682
697
  end
683
-
698
+
684
699
  def quote_table_name(name)
685
700
  schema, name_part = extract_pg_identifier_from_name(name.to_s)
686
-
701
+
687
702
  unless name_part
688
703
  quote_column_name(schema)
689
704
  else
@@ -691,11 +706,11 @@ module ::ArJdbc
691
706
  "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
692
707
  end
693
708
  end
694
-
709
+
695
710
  def quote_column_name(name)
696
711
  %("#{name.to_s.gsub("\"", "\"\"")}")
697
712
  end
698
-
713
+
699
714
  def quoted_date(value) #:nodoc:
700
715
  if value.acts_like?(:time) && value.respond_to?(:usec)
701
716
  "#{super}.#{sprintf("%06d", value.usec)}"
@@ -704,7 +719,7 @@ module ::ArJdbc
704
719
  end
705
720
  end
706
721
 
707
- def disable_referential_integrity(&block) #:nodoc:
722
+ def disable_referential_integrity # :nodoc:
708
723
  execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
709
724
  yield
710
725
  ensure
@@ -785,17 +800,22 @@ module ::ArJdbc
785
800
 
786
801
  # Maps logical Rails types to PostgreSQL-specific data types.
787
802
  def type_to_sql(type, limit = nil, precision = nil, scale = nil)
788
- return super unless type.to_s == 'integer'
789
- return 'integer' unless limit
790
-
791
- case limit
792
- when 1, 2; 'smallint'
793
- when 3, 4; 'integer'
794
- when 5..8; 'bigint'
795
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
803
+ case type.to_sym
804
+ when :integer
805
+ return 'integer' unless limit
806
+ case limit.to_i
807
+ when 1, 2; 'smallint'
808
+ when 3, 4; 'integer'
809
+ when 5..8; 'bigint'
810
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
811
+ end
812
+ when :binary
813
+ super(type, nil, nil, nil)
814
+ else
815
+ super
796
816
  end
797
817
  end
798
-
818
+
799
819
  def tables(name = nil)
800
820
  exec_query(<<-SQL, 'SCHEMA').map { |row| row["tablename"] }
801
821
  SELECT tablename
@@ -886,11 +906,12 @@ module ::ArJdbc
886
906
  end
887
907
  end
888
908
 
889
- # from rails postgresl_adapter
890
- def extract_table_ref_from_insert_sql(sql)
909
+ # taken from rails postgresql_adapter.rb
910
+ def extract_table_ref_from_insert_sql(sql) # :nodoc:
891
911
  sql[/into\s+([^\(]*).*values\s*\(/i]
892
912
  $1.strip if $1
893
913
  end
914
+
894
915
  end
895
916
  end
896
917
 
@@ -926,6 +947,7 @@ module ActiveRecord::ConnectionAdapters
926
947
 
927
948
  class PostgreSQLAdapter < JdbcAdapter
928
949
  include ArJdbc::PostgreSQL
950
+ include ArJdbc::PostgreSQL::ExplainSupport
929
951
 
930
952
  def initialize(*args)
931
953
  super
@@ -957,5 +979,31 @@ module ActiveRecord::ConnectionAdapters
957
979
  end
958
980
 
959
981
  alias_chained_method :columns, :query_cache, :pg_columns
982
+
983
+ # some QUOTING caching :
984
+
985
+ @@quoted_table_names = {}
986
+
987
+ def quote_table_name(name)
988
+ unless quoted = @@quoted_table_names[name]
989
+ quoted = super
990
+ @@quoted_table_names[name] = quoted.freeze
991
+ end
992
+ quoted
993
+ end
994
+
995
+ @@quoted_column_names = {}
996
+
997
+ def quote_column_name(name)
998
+ unless quoted = @@quoted_column_names[name]
999
+ quoted = super
1000
+ @@quoted_column_names[name] = quoted.freeze
1001
+ end
1002
+ quoted
1003
+ end
1004
+
960
1005
  end
961
1006
  end
1007
+
1008
+ # Don't need to load native postgres adapter
1009
+ $LOADED_FEATURES << 'active_record/connection_adapters/postgresql_adapter.rb'
@@ -1,11 +1,13 @@
1
- # Don't need to load native postgres adapter
2
- $LOADED_FEATURES << "active_record/connection_adapters/postgresql_adapter.rb"
3
-
4
1
  class ActiveRecord::Base
5
2
  class << self
6
3
  def postgresql_connection(config)
7
- require 'active_record/connection_adapters/jdbcpostgresql_adapter'
4
+ begin
5
+ require 'jdbc/postgres'
6
+ ::Jdbc::Postgres.load_driver(:require) if defined?(::Jdbc::Postgres.load_driver)
7
+ rescue LoadError # assuming driver.jar is on the class-path
8
+ end
8
9
 
10
+ config[:username] ||= Java::JavaLang::System.get_property("user.name")
9
11
  config[:host] ||= "localhost"
10
12
  config[:port] ||= 5432
11
13
  config[:url] ||= "jdbc:postgresql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
@@ -0,0 +1,55 @@
1
+ module ::ArJdbc
2
+ module PostgreSQL
3
+ module ExplainSupport
4
+ def supports_explain?
5
+ true
6
+ end
7
+
8
+ def explain(arel, binds = [])
9
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
10
+ raw_result = execute(sql, "EXPLAIN", binds)
11
+ # TODO we should refactor to exce_query once it returns Result ASAP :
12
+ keys = raw_result[0] ? raw_result[0].keys : {}
13
+ rows = raw_result.map { |hash| hash.values }
14
+ ExplainPrettyPrinter.new.pp ActiveRecord::Result.new(keys, rows)
15
+ end
16
+
17
+ class ExplainPrettyPrinter # :nodoc:
18
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
19
+ # PostgreSQL shell:
20
+ #
21
+ # QUERY PLAN
22
+ # ------------------------------------------------------------------------------
23
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
24
+ # Join Filter: (posts.user_id = users.id)
25
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
26
+ # Index Cond: (id = 1)
27
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
28
+ # Filter: (posts.user_id = 1)
29
+ # (6 rows)
30
+ #
31
+ def pp(result)
32
+ header = result.columns.first
33
+ lines = result.rows.map(&:first)
34
+
35
+ # We add 2 because there's one char of padding at both sides, note
36
+ # the extra hyphens in the example above.
37
+ width = [header, *lines].map(&:length).max + 2
38
+
39
+ pp = []
40
+
41
+ pp << header.center(width).rstrip
42
+ pp << '-' * width
43
+
44
+ pp += lines.map {|line| " #{line}"}
45
+
46
+ nrows = result.rows.length
47
+ rows_label = nrows == 1 ? 'row' : 'rows'
48
+ pp << "(#{nrows} #{rows_label})"
49
+
50
+ pp.join("\n") + "\n"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end