sequel 4.3.0 → 4.4.0

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 (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