sequel 4.3.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +34 -0
  3. data/README.rdoc +7 -7
  4. data/Rakefile +2 -2
  5. data/doc/active_record.rdoc +2 -2
  6. data/doc/association_basics.rdoc +21 -7
  7. data/doc/bin_sequel.rdoc +2 -2
  8. data/doc/cheat_sheet.rdoc +2 -1
  9. data/doc/dataset_basics.rdoc +1 -1
  10. data/doc/dataset_filtering.rdoc +1 -1
  11. data/doc/migration.rdoc +2 -2
  12. data/doc/object_model.rdoc +2 -2
  13. data/doc/opening_databases.rdoc +13 -1
  14. data/doc/querying.rdoc +9 -4
  15. data/doc/release_notes/4.4.0.txt +92 -0
  16. data/doc/schema_modification.rdoc +1 -1
  17. data/doc/security.rdoc +2 -2
  18. data/doc/sql.rdoc +3 -3
  19. data/doc/thread_safety.rdoc +1 -1
  20. data/doc/validations.rdoc +1 -1
  21. data/doc/virtual_rows.rdoc +1 -1
  22. data/lib/sequel/adapters/jdbc.rb +85 -19
  23. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  24. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  25. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  26. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  27. data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
  28. data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
  29. data/lib/sequel/adapters/jdbc/postgresql.rb +34 -3
  30. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +57 -0
  31. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  32. data/lib/sequel/adapters/oracle.rb +1 -1
  33. data/lib/sequel/adapters/shared/db2.rb +5 -0
  34. data/lib/sequel/adapters/shared/oracle.rb +41 -4
  35. data/lib/sequel/adapters/shared/sqlanywhere.rb +458 -0
  36. data/lib/sequel/adapters/sqlanywhere.rb +177 -0
  37. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +11 -3
  38. data/lib/sequel/core.rb +4 -4
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/query.rb +1 -1
  41. data/lib/sequel/database/schema_generator.rb +1 -1
  42. data/lib/sequel/database/schema_methods.rb +2 -2
  43. data/lib/sequel/dataset.rb +1 -1
  44. data/lib/sequel/dataset/actions.rb +2 -0
  45. data/lib/sequel/dataset/graph.rb +1 -1
  46. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  47. data/lib/sequel/dataset/query.rb +37 -16
  48. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  49. data/lib/sequel/extensions/date_arithmetic.rb +2 -2
  50. data/lib/sequel/extensions/migration.rb +1 -1
  51. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +5 -4
  52. data/lib/sequel/extensions/pg_array.rb +2 -2
  53. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  54. data/lib/sequel/extensions/pg_hstore.rb +2 -2
  55. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
  56. data/lib/sequel/extensions/pg_json.rb +2 -2
  57. data/lib/sequel/extensions/pg_json_ops.rb +2 -2
  58. data/lib/sequel/extensions/pg_range.rb +2 -2
  59. data/lib/sequel/extensions/pg_range_ops.rb +2 -2
  60. data/lib/sequel/extensions/pg_row.rb +2 -2
  61. data/lib/sequel/extensions/pg_row_ops.rb +3 -3
  62. data/lib/sequel/model.rb +1 -1
  63. data/lib/sequel/model/associations.rb +106 -17
  64. data/lib/sequel/model/base.rb +23 -19
  65. data/lib/sequel/plugins/json_serializer.rb +1 -1
  66. data/lib/sequel/plugins/many_through_many.rb +14 -6
  67. data/lib/sequel/plugins/pg_array_associations.rb +28 -0
  68. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  69. data/lib/sequel/plugins/serialization.rb +11 -0
  70. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  71. data/lib/sequel/plugins/table_select.rb +41 -0
  72. data/lib/sequel/plugins/tree.rb +1 -1
  73. data/lib/sequel/sql.rb +2 -2
  74. data/lib/sequel/version.rb +1 -1
  75. data/spec/adapters/oracle_spec.rb +22 -1
  76. data/spec/adapters/postgres_spec.rb +31 -48
  77. data/spec/adapters/sqlanywhere_spec.rb +170 -0
  78. data/spec/core/dataset_spec.rb +109 -0
  79. data/spec/core/object_graph_spec.rb +7 -0
  80. data/spec/extensions/constraint_validations_spec.rb +7 -0
  81. data/spec/extensions/core_refinements_spec.rb +1 -1
  82. data/spec/extensions/many_through_many_spec.rb +65 -0
  83. data/spec/extensions/pg_array_associations_spec.rb +44 -0
  84. data/spec/extensions/rcte_tree_spec.rb +3 -3
  85. data/spec/extensions/spec_helper.rb +1 -1
  86. data/spec/extensions/table_select_spec.rb +71 -0
  87. data/spec/integration/associations_test.rb +279 -7
  88. data/spec/integration/dataset_test.rb +13 -4
  89. data/spec/integration/schema_test.rb +12 -14
  90. data/spec/model/associations_spec.rb +472 -3
  91. data/spec/model/class_dataset_methods_spec.rb +1 -0
  92. data/spec/model/model_spec.rb +10 -0
  93. metadata +10 -2
@@ -586,7 +586,7 @@ the table if the table already exists. On some databases, it uses
586
586
  <tt>IF NOT EXISTS</tt>, on others it does a separate query to check for
587
587
  existence.
588
588
 
589
- This should not be used inside migrations, as if the the tbale does not
589
+ This should not be used inside migrations, as if the table does not
590
590
  exist, it may mess up the migration.
591
591
 
592
592
  === +rename_table+
@@ -79,7 +79,7 @@ in which case Sequel automatically literalizes the input:
79
79
  Sequel generally treats ruby strings as SQL strings (escaping them correctly), and
80
80
  not as raw SQL. However, you can convert a ruby string to a literal string, and
81
81
  Sequel will then treat it as raw SQL. This is typically done through String#lit
82
- if the {core_extensions}[link:files/doc/core_extensions_rdoc.html] are in use,
82
+ if the {core_extensions}[rdoc-ref:doc/core_extensions.rdoc] are in use,
83
83
  or Sequel.lit[rdoc-ref:Sequel::SQL::Builders#lit] if they are not in use.
84
84
 
85
85
  'a'.lit
@@ -308,7 +308,7 @@ practice, though being explicit on a per-call basis is still recommended:
308
308
  Album.set_allowed_columns(:name, :copies_sold)
309
309
  Album.create(params[:album]) # Only name and copies_sold set
310
310
 
311
- For more details on the mass assignment methods, see the {Mass Assignment Guide}[link:files/doc/mass_assignment_rdoc.html].
311
+ For more details on the mass assignment methods, see the {Mass Assignment Guide}[rdoc-ref:doc/mass_assignment.rdoc].
312
312
 
313
313
  == General Parameter Handling
314
314
 
@@ -78,7 +78,7 @@ Almost everywhere in Sequel, you can drop down to literal SQL by providing a lit
78
78
  DB[:albums].select('name') # SELECT 'name' FROM albums
79
79
  DB[:albums].select(Sequel.lit('name')) # SELECT name FROM albums
80
80
 
81
- For a simpler way of creating literal strings, you can also use the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html], which adds the <tt>String#lit</tt> method, and other methods that integrate Sequel's DSL with the ruby language:
81
+ For a simpler way of creating literal strings, you can also use the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc], which adds the <tt>String#lit</tt> method, and other methods that integrate Sequel's DSL with the ruby language:
82
82
 
83
83
  DB[:albums].select('name'.lit)
84
84
 
@@ -137,7 +137,7 @@ The other way to qualify an identifier is to use the <tt>Sequel.qualify</tt> wit
137
137
 
138
138
  Sequel.qualify(:table, :column) # "table"."column"
139
139
 
140
- Another way to generate identifiers is to use Sequel's {virtual row support}[link:files/doc/virtual_rows_rdoc.html]:
140
+ Another way to generate identifiers is to use Sequel's {virtual row support}[rdoc-ref:doc/virtual_rows.rdoc]:
141
141
 
142
142
  DB[:albums].select{name} # SELECT "name" FROM "albums"
143
143
  DB[:albums].select{albums__name} # SELECT "albums"."name" FROM "albums"
@@ -573,4 +573,4 @@ Note how +update+ and +delete+ used the +where+ argument, but that +insert+ did
573
573
 
574
574
  === Methods Used for Each SQL Clause
575
575
 
576
- To see which methods exist that affect each SQL clause, see the {"Dataset Basics" guide}[link:files/doc/dataset_basics_rdoc.html].
576
+ To see which methods exist that affect each SQL clause, see the {"Dataset Basics" guide}[rdoc-ref:doc/dataset_basics.rdoc].
@@ -4,7 +4,7 @@ Most Sequel usage (and all common Sequel usage) is thread safe by default. Spec
4
4
 
5
5
  == Connection Pool
6
6
 
7
- In order to allow multiple threads to operate on the same database at the same time, Sequel uses a connection pool. The connection pool is designed so that a thread uses a connection for the minimum amount of time, returning the connection to the pool as soon as it is done using the connection. If a thread requests a connection and the pool does not have an available connection, a new connection will be created. If the maximum number of connections in the pool has already been reached, the thread will block (actually busy-wait) until a connection is available or the the connection pool timeout has elapsed (in which case a PoolTimeout error will be raised).
7
+ In order to allow multiple threads to operate on the same database at the same time, Sequel uses a connection pool. The connection pool is designed so that a thread uses a connection for the minimum amount of time, returning the connection to the pool as soon as it is done using the connection. If a thread requests a connection and the pool does not have an available connection, a new connection will be created. If the maximum number of connections in the pool has already been reached, the thread will block (actually busy-wait) until a connection is available or the connection pool timeout has elapsed (in which case a PoolTimeout error will be raised).
8
8
 
9
9
  == Exceptions
10
10
 
@@ -299,7 +299,7 @@ Additionally, you can also include an optional options hash as the last argument
299
299
  :message :: The message to use
300
300
  :only_if_modified :: Only check the uniqueness if the object is new or one of the columns has been modified.
301
301
 
302
- +validates_unique+ is the only method in +validation_helpers+ that checks with the database. Attempting to validate uniqueness outside of the database suffers from a race condition, so any time you want to add a uniqueness validation, you should make sure to add a uniqueness constraint or unique index on the underlying database table. See the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html] for details on how to do that.
302
+ +validates_unique+ is the only method in +validation_helpers+ that checks with the database. Attempting to validate uniqueness outside of the database suffers from a race condition, so any time you want to add a uniqueness validation, you should make sure to add a uniqueness constraint or unique index on the underlying database table. See the {"Migrations and Schema Modification" guide}[rdoc-ref:doc/migration.rdoc] for details on how to do that.
303
303
 
304
304
  == +validation_helpers+ Options
305
305
 
@@ -68,7 +68,7 @@ inside the proc. If that doesn't make sense, maybe this example will help:
68
68
  # WHERE c > (a - 32)
69
69
 
70
70
  There are two related differences here. First is the usage of <tt>o.c</tt> vs +c+,
71
- and second is the difference between the the use of +a+. In the regular proc,
71
+ and second is the difference between the use of +a+. In the regular proc,
72
72
  you couldn't call +c+ without an explicit receiver in the proc, unless the self of the
73
73
  surrounding scope responded to it. For +a+, note how ruby calls the method on
74
74
  the receiver of the surrounding scope in the regular proc, which returns an integer,
@@ -143,6 +143,25 @@ module Sequel
143
143
  db.extend(Sequel::JDBC::Cubrid::DatabaseMethods)
144
144
  db.extend_datasets Sequel::Cubrid::DatasetMethods
145
145
  Java::cubrid.jdbc.driver.CUBRIDDriver
146
+ end,
147
+ :sqlanywhere=>proc do |db|
148
+ drv = [
149
+ lambda{Java::sybase.jdbc4.sqlanywhere.IDriver},
150
+ lambda{Java::ianywhere.ml.jdbcodbc.jdbc4.IDriver},
151
+ lambda{Java::sybase.jdbc.sqlanywhere.IDriver},
152
+ lambda{Java::ianywhere.ml.jdbcodbc.jdbc.IDriver},
153
+ lambda{Java::com.sybase.jdbc4.jdbc.Sybdriver},
154
+ lambda{Java::com.sybase.jdbc3.jdbc.Sybdriver}
155
+ ].each do |class_proc|
156
+ begin
157
+ break class_proc.call
158
+ rescue NameError
159
+ end
160
+ end
161
+ Sequel.require 'adapters/jdbc/sqlanywhere'
162
+ db.extend(Sequel::JDBC::SqlAnywhere::DatabaseMethods)
163
+ db.dataset_class = Sequel::JDBC::SqlAnywhere::Dataset
164
+ drv
146
165
  end
147
166
  }
148
167
 
@@ -286,15 +305,31 @@ module Sequel
286
305
  def execute_insert(sql, opts=OPTS)
287
306
  execute(sql, {:type=>:insert}.merge(opts))
288
307
  end
289
-
308
+
309
+ # Use the JDBC metadata to get a list of foreign keys for the table.
310
+ def foreign_key_list(table, opts=OPTS)
311
+ m = output_identifier_meth
312
+ schema, table = metadata_schema_and_table(table, opts)
313
+ foreign_keys = {}
314
+ metadata(:getImportedKeys, nil, schema, table) do |r|
315
+ if fk = foreign_keys[r[:fk_name]]
316
+ fk[:columns] << [r[:key_seq], m.call(r[:fkcolumn_name])]
317
+ fk[:key] << [r[:key_seq], m.call(r[:pkcolumn_name])]
318
+ elsif r[:fk_name]
319
+ foreign_keys[r[:fk_name]] = {:name=>m.call(r[:fk_name]), :columns=>[[r[:key_seq], m.call(r[:fkcolumn_name])]], :table=>m.call(r[:pktable_name]), :key=>[[r[:key_seq], m.call(r[:pkcolumn_name])]]}
320
+ end
321
+ end
322
+ foreign_keys.values.each do |fk|
323
+ [:columns, :key].each do |k|
324
+ fk[k] = fk[k].sort.map{|_, v| v}
325
+ end
326
+ end
327
+ end
328
+
290
329
  # Use the JDBC metadata to get the index information for the table.
291
330
  def indexes(table, opts=OPTS)
292
331
  m = output_identifier_meth
293
- im = input_identifier_meth
294
- schema, table = schema_and_table(table)
295
- schema ||= opts[:schema]
296
- schema = im.call(schema) if schema
297
- table = im.call(table)
332
+ schema, table = metadata_schema_and_table(table, opts)
298
333
  indexes = {}
299
334
  metadata(:getIndexInfo, nil, schema, table, false, true) do |r|
300
335
  next unless name = r[:column_name]
@@ -511,6 +546,16 @@ module Sequel
511
546
  end
512
547
  end
513
548
 
549
+ # Return the schema and table suitable for use with metadata queries.
550
+ def metadata_schema_and_table(table, opts)
551
+ im = input_identifier_meth(opts[:dataset])
552
+ schema, table = schema_and_table(table)
553
+ schema ||= opts[:schema]
554
+ schema = im.call(schema) if schema
555
+ table = im.call(table)
556
+ [schema, table]
557
+ end
558
+
514
559
  # Created a JDBC prepared statement on the connection with the given SQL.
515
560
  def prepare_jdbc_statement(conn, sql, opts)
516
561
  conn.prepareStatement(sql)
@@ -563,12 +608,8 @@ module Sequel
563
608
  # Parse the table schema for the given table.
564
609
  def schema_parse_table(table, opts=OPTS)
565
610
  m = output_identifier_meth(opts[:dataset])
566
- im = input_identifier_meth(opts[:dataset])
567
611
  ds = dataset
568
- schema, table = schema_and_table(table)
569
- schema ||= opts[:schema]
570
- schema = im.call(schema) if schema
571
- table = im.call(table)
612
+ schema, table = metadata_schema_and_table(table, opts)
572
613
  pks, ts = [], []
573
614
  metadata(:getPrimaryKeys, nil, schema, table) do |h|
574
615
  next if schema_parse_table_skip?(h, schema)
@@ -742,7 +783,7 @@ module Sequel
742
783
  # Return a callable object that will convert any value of <tt>v</tt>'s
743
784
  # class to a ruby object. If no callable object can handle <tt>v</tt>'s
744
785
  # class, return false so that the negative lookup is cached.
745
- def convert_type_proc(v)
786
+ def convert_type_proc(v, ctn=nil)
746
787
  case v
747
788
  when JAVA_BIG_DECIMAL
748
789
  DECIMAL_METHOD
@@ -768,6 +809,16 @@ module Sequel
768
809
  false
769
810
  end
770
811
  end
812
+
813
+ # The generic JDBC support does not use column info when deciding on conversion procs.
814
+ def convert_type_proc_uses_column_info?
815
+ false
816
+ end
817
+
818
+ # By default, if using column info, assume the info needed is the column's type name.
819
+ def convert_type_proc_column_info(meta, i)
820
+ meta.column_type_name(i)
821
+ end
771
822
 
772
823
  # Extend the dataset with the JDBC stored procedure methods.
773
824
  def prepare_extend_sproc(ds)
@@ -781,14 +832,29 @@ module Sequel
781
832
  meta = result.getMetaData
782
833
  cols = []
783
834
  i = 0
784
- meta.getColumnCount.times{cols << [output_identifier(meta.getColumnLabel(i+=1)), i]}
785
- columns = cols.map{|c| c.at(0)}
786
- @columns = columns
787
835
  ct = @convert_types
788
- if (ct.nil? ? db.convert_types : ct)
789
- cols.each{|c| c << nil}
836
+ ct = db.convert_types if ct.nil?
837
+
838
+ if ct
839
+ use_column_info = convert_type_proc_uses_column_info?
840
+ # When converting types, four values are associated with every column:
841
+ # 1) column name symbol
842
+ # 2) index (starting at 1, as JDBC does)
843
+ # 3) database-specific value usable by convert_type_proc to determine the conversion proc to use for column
844
+ # 4) nil (later updated with the actual conversion proc during the lookup process)
845
+ meta.getColumnCount.times do
846
+ i += 1
847
+ cols << [output_identifier(meta.getColumnLabel(i)), i, (convert_type_proc_column_info(meta, i) if use_column_info), nil]
848
+ end
849
+ @columns = cols.map{|c| c.at(0)}
790
850
  process_result_set_convert(cols, result, &block)
791
851
  else
852
+ # When not converting types, only the type name and the index are used.
853
+ meta.getColumnCount.times do
854
+ i += 1
855
+ cols << [output_identifier(meta.getColumnLabel(i)), i]
856
+ end
857
+ @columns = cols.map{|c| c.at(0)}
792
858
  process_result_set_no_convert(cols, result, &block)
793
859
  end
794
860
  ensure
@@ -816,13 +882,13 @@ module Sequel
816
882
  def process_result_set_convert(cols, result)
817
883
  while result.next
818
884
  row = {}
819
- cols.each do |n, i, p|
885
+ cols.each do |n, i, ctn, p|
820
886
  v = result.getObject(i)
821
887
  row[n] = if v
822
888
  if p
823
889
  p.call(v)
824
890
  elsif p.nil?
825
- cols[i-1][2] = p = convert_type_proc(v)
891
+ cols[i-1][3] = p = convert_type_proc(v, ctn)
826
892
  if p
827
893
  p.call(v)
828
894
  else
@@ -65,7 +65,7 @@ module Sequel
65
65
  private
66
66
 
67
67
  # Return clob as blob if use_clob_as_blob is true
68
- def convert_type_proc(v)
68
+ def convert_type_proc(v, ctn=nil)
69
69
  case v
70
70
  when JAVA_SQL_CLOB
71
71
  ::Sequel::DB2::use_clob_as_blob ? DB2_CLOB_METHOD : super
@@ -255,7 +255,7 @@ module Sequel
255
255
  DERBY_CLOB_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:derby_clob)
256
256
 
257
257
  # Handle clobs on Derby as strings.
258
- def convert_type_proc(v)
258
+ def convert_type_proc(v, ctn=nil)
259
259
  if v.is_a?(JAVA_SQL_CLOB)
260
260
  DERBY_CLOB_METHOD
261
261
  else
@@ -62,7 +62,7 @@ module Sequel
62
62
  when :add_column
63
63
  if (pk = op.delete(:primary_key)) || (ref = op.delete(:table))
64
64
  sqls = [super(table, op)]
65
- sqls << "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})" if pk
65
+ sqls << "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})" if pk && op[:type] != :identity
66
66
  if ref
67
67
  op[:table] = ref
68
68
  sqls << "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])}) #{column_references_sql(op)}"
@@ -202,7 +202,7 @@ module Sequel
202
202
  H2_CLOB_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:h2_clob)
203
203
 
204
204
  # Handle H2 specific clobs as strings.
205
- def convert_type_proc(v)
205
+ def convert_type_proc(v, ctn=nil)
206
206
  if v.is_a?(Java::OrgH2Jdbc::JdbcClob)
207
207
  H2_CLOB_METHOD
208
208
  else
@@ -37,6 +37,13 @@ module Sequel
37
37
  # HSQLDB specific SQL for renaming columns, and changing column types and/or nullity.
38
38
  def alter_table_sql(table, op)
39
39
  case op[:op]
40
+ when :add_column
41
+ if op[:table]
42
+ [super(table, op.merge(:table=>nil)),
43
+ alter_table_sql(table, op.merge(:op=>:add_constraint, :type=>:foreign_key, :name=>op[:foreign_key_name], :columns=>[op[:name]], :table=>op[:table]))]
44
+ else
45
+ super
46
+ end
40
47
  when :rename_column
41
48
  "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} RENAME TO #{quote_identifier(op[:new_name])}"
42
49
  when :set_column_type
@@ -33,7 +33,7 @@ module Sequel
33
33
  JTDS_CLOB_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:jtds_clob)
34
34
 
35
35
  # Handle CLOB types retrieved via JTDS.
36
- def convert_type_proc(v)
36
+ def convert_type_proc(v, ctn=nil)
37
37
  if v.is_a?(Java::NetSourceforgeJtdsJdbc::ClobImpl)
38
38
  JTDS_CLOB_METHOD
39
39
  else
@@ -112,7 +112,7 @@ module Sequel
112
112
  convert_type_oracle_timestamp(db.synchronize(@opts[:server]){|c| v.timestampValue(c)})
113
113
  end
114
114
 
115
- def convert_type_proc(v)
115
+ def convert_type_proc(v, ctn=nil)
116
116
  case v
117
117
  when JAVA_BIG_DECIMAL
118
118
  ORACLE_DECIMAL_METHOD
@@ -168,17 +168,48 @@ module Sequel
168
168
  # Handle PostgreSQL array and object types. Object types are just
169
169
  # turned into strings, similarly to how the native adapter treats
170
170
  # the types.
171
- def convert_type_proc(v)
171
+ def convert_type_proc(v, ctn=nil)
172
172
  case v
173
173
  when Java::OrgPostgresqlJdbc4::Jdbc4Array
174
- PGArrayConverter.new(method(:convert_type_proc))
174
+ if pr = db.conversion_procs[ctn]
175
+ lambda{|x| pr.call(PG_OBJECT_METHOD.call(x))}
176
+ else
177
+ PGArrayConverter.new(method(:convert_type_proc))
178
+ end
175
179
  when Java::OrgPostgresqlUtil::PGobject
176
- PG_OBJECT_METHOD
180
+ if pr = db.conversion_procs[ctn]
181
+ lambda{|x| pr.call(PG_OBJECT_METHOD.call(x))}
182
+ else
183
+ PG_OBJECT_METHOD
184
+ end
185
+ when String
186
+ if pr = db.conversion_procs[ctn]
187
+ pr
188
+ else
189
+ false
190
+ end
191
+ when JAVA_HASH_MAP
192
+ if Sequel.respond_to?(:hstore)
193
+ lambda{|x| Sequel.hstore(HASH_MAP_METHOD.call(x))}
194
+ else
195
+ HASH_MAP_METHOD
196
+ end
177
197
  else
178
198
  super
179
199
  end
180
200
  end
181
201
 
202
+ # The jdbc/postgresql adapter uses column type oids when determining
203
+ # conversion procs.
204
+ def convert_type_proc_uses_column_info?
205
+ true
206
+ end
207
+
208
+ # Use the column type oid as the database specific column info value.
209
+ def convert_type_proc_column_info(meta, i)
210
+ meta.field(i).oid
211
+ end
212
+
182
213
  # Literalize strings similar to the native postgres adapter
183
214
  def literal_string_append(sql, v)
184
215
  sql << APOS << db.synchronize(@opts[:server]){|c| c.escape_string(v)} << APOS
@@ -0,0 +1,57 @@
1
+ Sequel.require 'adapters/shared/sqlanywhere'
2
+ Sequel.require 'adapters/jdbc/transactions'
3
+
4
+ module Sequel
5
+ module JDBC
6
+ module SqlAnywhere
7
+ # Database instance methods for Sybase databases accessed via JDBC.
8
+ module DatabaseMethods
9
+ extend Sequel::Database::ResetIdentifierMangling
10
+ include Sequel::SqlAnywhere::DatabaseMethods
11
+ include Sequel::JDBC::Transactions
12
+
13
+ LAST_INSERT_ID = 'SELECT @@IDENTITY'.freeze
14
+
15
+ private
16
+
17
+ # Get the last inserted id.
18
+ def last_insert_id(conn, opts=OPTS)
19
+ statement(conn) do |stmt|
20
+ sql = LAST_INSERT_ID
21
+ rs = log_yield(sql){stmt.executeQuery(sql)}
22
+ rs.next
23
+ rs.getInt(1)
24
+ end
25
+ end
26
+ end
27
+
28
+ #Dataset class for Sybase datasets accessed via JDBC.
29
+ class Dataset < JDBC::Dataset
30
+ include Sequel::SqlAnywhere::DatasetMethods
31
+
32
+ private
33
+
34
+ class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
35
+ def sqla_boolean(i) i != 0 end
36
+ end
37
+
38
+ BOOLEAN_METHOD = TYPE_TRANSLATOR_INSTANCE.method(:sqla_boolean)
39
+
40
+ def convert_type_proc(v, ctn=nil)
41
+ case
42
+ when ctn && ctn =~ SqlAnywhere::DatabaseMethods::SMALLINT_RE
43
+ BOOLEAN_METHOD
44
+ else
45
+ super(v, ctn)
46
+ end
47
+ end
48
+
49
+ # SQLAnywhere needs the column info if it is converting smallint to bool,
50
+ # since the JDBC adapter always returns smallint as integer.
51
+ def convert_type_proc_uses_column_info?
52
+ convert_smallint_to_bool
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -22,13 +22,13 @@ module Sequel
22
22
  def process_result_set_convert(cols, result)
23
23
  while result.next
24
24
  row = {}
25
- cols.each do |n, i, p|
25
+ cols.each do |n, i, ctn, p|
26
26
  v = (n == :is_autoincrement ? result.getString(i) : result.getObject(i))
27
27
  row[n] = if v
28
28
  if p
29
29
  p.call(v)
30
30
  elsif p.nil?
31
- cols[i-1][2] = p = convert_type_proc(v)
31
+ cols[i-1][3] = p = convert_type_proc(v, ctn)
32
32
  if p
33
33
  p.call(v)
34
34
  else