class Sequel::JDBC::Database

Attributes

basic_type_convertor_map[R]

Map of JDBC type ids to callable objects that return appropriate ruby or java values.

convert_types[RW]

Whether to convert some Java types to ruby types when retrieving rows. True by default, can be set to false to roughly double performance when fetching rows.

driver[R]

The Java database driver we are using (should be a Java class)

fetch_size[RW]

The fetch size to use for JDBC Statement objects created by this database. By default, this is nil so a fetch size is not set explicitly.

type_convertor_map[R]

Map of JDBC type ids to callable objects that return appropriate ruby values.

Public Instance Methods

call_sproc(name, opts = OPTS) { || ... } click to toggle source

Execute the given stored procedure with the give name. If a block is given, the stored procedure should return rows.

    # File lib/sequel/adapters/jdbc.rb
187 def call_sproc(name, opts = OPTS)
188   args = opts[:args] || []
189   sql = "{call #{name}(#{args.map{'?'}.join(',')})}"
190   synchronize(opts[:server]) do |conn|
191     begin
192       cps = conn.prepareCall(sql)
193 
194       i = 0
195       args.each{|arg| set_ps_arg(cps, arg, i+=1)}
196 
197       if defined?(yield)
198         yield log_connection_yield(sql, conn){cps.executeQuery}
199       else
200         log_connection_yield(sql, conn){cps.executeUpdate}
201         if opts[:type] == :insert
202           last_insert_id(conn, opts)
203         end
204       end
205     rescue *DATABASE_ERROR_CLASSES => e
206       raise_error(e)
207     ensure
208       cps.close if cps
209     end
210   end
211 end
connect(server) click to toggle source

Connect to the database using JavaSQL::DriverManager.getConnection, and falling back to driver.new.connect if the driver is known.

    # File lib/sequel/adapters/jdbc.rb
215 def connect(server)
216   opts = server_opts(server)
217   conn = if jndi?
218     get_connection_from_jndi
219   else
220     args = [uri(opts)]
221     args.concat([opts[:user], opts[:password]]) if opts[:user] && opts[:password]
222     begin
223       JavaSQL::DriverManager.setLoginTimeout(opts[:login_timeout]) if opts[:login_timeout]
224       raise StandardError, "skipping regular connection" if opts[:jdbc_properties]
225       JavaSQL::DriverManager.getConnection(*args)
226     rescue StandardError, *DATABASE_ERROR_CLASSES => e
227       raise e unless driver
228       # If the DriverManager can't get the connection - use the connect
229       # method of the driver. (This happens under Tomcat for instance)
230       props = java.util.Properties.new
231       if opts && opts[:user] && opts[:password]
232         props.setProperty("user", opts[:user])
233         props.setProperty("password", opts[:password])
234       end
235       opts[:jdbc_properties].each{|k,v| props.setProperty(k.to_s, v)} if opts[:jdbc_properties]
236       begin
237         c = driver.new.connect(args[0], props)
238         raise(Sequel::DatabaseError, 'driver.new.connect returned nil: probably bad JDBC connection string') unless c
239         c
240       rescue StandardError, *DATABASE_ERROR_CLASSES => e2
241         if e2.respond_to?(:message=) && e2.message != e.message
242           e2.message = "#{e2.message}\n#{e.class.name}: #{e.message}"
243         end
244         raise e2
245       end
246     end
247   end
248   setup_connection_with_opts(conn, opts)
249 end
disconnect_connection(c) click to toggle source

Close given adapter connections, and delete any related prepared statements.

    # File lib/sequel/adapters/jdbc.rb
252 def disconnect_connection(c)
253   @connection_prepared_statements_mutex.synchronize{@connection_prepared_statements.delete(c)}
254   c.close
255 end
execute(sql, opts=OPTS) { |log_connection_yield(sql, conn){executeQuery(sql)}| ... } click to toggle source
    # File lib/sequel/adapters/jdbc.rb
257 def execute(sql, opts=OPTS, &block)
258   return call_sproc(sql, opts, &block) if opts[:sproc]
259   return execute_prepared_statement(sql, opts, &block) if [Symbol, Dataset].any?{|c| sql.is_a?(c)}
260   synchronize(opts[:server]) do |conn|
261     statement(conn) do |stmt|
262       if block
263         if size = fetch_size
264           stmt.setFetchSize(size)
265         end
266         yield log_connection_yield(sql, conn){stmt.executeQuery(sql)}
267       else
268         case opts[:type]
269         when :ddl
270           log_connection_yield(sql, conn){stmt.execute(sql)}
271         when :insert
272           log_connection_yield(sql, conn){execute_statement_insert(stmt, sql)}
273           opts = Hash[opts]
274           opts[:stmt] = stmt
275           last_insert_id(conn, opts)
276         else
277           log_connection_yield(sql, conn){stmt.executeUpdate(sql)}
278         end
279       end
280     end
281   end
282 end
Also aliased as: execute_dui
execute_ddl(sql, opts=OPTS) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
285 def execute_ddl(sql, opts=OPTS)
286   opts = Hash[opts]
287   opts[:type] = :ddl
288   execute(sql, opts)
289 end
execute_dui(sql, opts=OPTS, &block)
Alias for: execute
execute_insert(sql, opts=OPTS) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
291 def execute_insert(sql, opts=OPTS)
292   opts = Hash[opts]
293   opts[:type] = :insert
294   execute(sql, opts)
295 end
foreign_key_list(table, opts=OPTS) click to toggle source

Use the JDBC metadata to get a list of foreign keys for the table.

    # File lib/sequel/adapters/jdbc.rb
304 def foreign_key_list(table, opts=OPTS)
305   m = output_identifier_meth
306   schema, table = metadata_schema_and_table(table, opts)
307   foreign_keys = {}
308   metadata(:getImportedKeys, nil, schema, table) do |r|
309     if fk = foreign_keys[r[:fk_name]]
310       fk[:columns] << [r[:key_seq], m.call(r[:fkcolumn_name])]
311       fk[:key] << [r[:key_seq], m.call(r[:pkcolumn_name])]
312     elsif r[:fk_name]
313       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])]]}
314     end
315   end
316   foreign_keys.values.each do |fk|
317     [:columns, :key].each do |k|
318       fk[k] = fk[k].sort.map{|_, v| v}
319     end
320   end
321 end
freeze() click to toggle source
Calls superclass method Sequel::Database#freeze
    # File lib/sequel/adapters/jdbc.rb
297 def freeze
298   @type_convertor_map.freeze
299   @basic_type_convertor_map.freeze
300   super
301 end
indexes(table, opts=OPTS) click to toggle source

Use the JDBC metadata to get the index information for the table.

    # File lib/sequel/adapters/jdbc.rb
324 def indexes(table, opts=OPTS)
325   m = output_identifier_meth
326   schema, table = metadata_schema_and_table(table, opts)
327   indexes = {}
328   metadata(:getIndexInfo, nil, schema, table, false, true) do |r|
329     next unless name = r[:column_name]
330     next if respond_to?(:primary_key_index_re, true) and r[:index_name] =~ primary_key_index_re 
331     i = indexes[m.call(r[:index_name])] ||= {:columns=>[], :unique=>[false, 0].include?(r[:non_unique])}
332     i[:columns] << m.call(name)
333   end
334   indexes
335 end
jndi?() click to toggle source

Whether or not JNDI is being used for this connection.

    # File lib/sequel/adapters/jdbc.rb
338 def jndi?
339   !!(uri =~ JNDI_URI_REGEXP)
340 end
tables(opts=OPTS) click to toggle source

All tables in this database

    # File lib/sequel/adapters/jdbc.rb
343 def tables(opts=OPTS)
344   get_tables('TABLE', opts)
345 end
uri(opts=OPTS) click to toggle source

The uri for this connection. You can specify the uri using the :uri, :url, or :database options. You don’t need to worry about this if you use Sequel.connect with the JDBC connectrion strings.

    # File lib/sequel/adapters/jdbc.rb
351 def uri(opts=OPTS)
352   opts = @opts.merge(opts)
353   ur = opts[:uri] || opts[:url] || opts[:database]
354   ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
355 end
views(opts=OPTS) click to toggle source

All views in this database

    # File lib/sequel/adapters/jdbc.rb
358 def views(opts=OPTS)
359   get_tables('VIEW', opts)
360 end

Private Instance Methods

_database_exception_sqlstate(exception, opts) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
403 def _database_exception_sqlstate(exception, opts)
404   16.times do
405     return exception.getSQLState if exception.respond_to?(:getSQLState)
406     break unless exception.respond_to?(:cause) && (exception = exception.cause)
407   end
408 
409   nil
410 end
adapter_initialize() click to toggle source

Call the DATABASE_SETUP proc directly after initialization, so the object always uses sub adapter specific code. Also, raise an error immediately if the connection doesn’t have a uri, since JDBC requires one.

    # File lib/sequel/adapters/jdbc.rb
368 def adapter_initialize
369   @connection_prepared_statements = {}
370   @connection_prepared_statements_mutex = Mutex.new
371   @fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size
372   @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
373   raise(Error, "No connection string specified") unless uri
374   
375   resolved_uri = jndi? ? get_uri_from_jndi : uri
376   setup_type_convertor_map_early
377 
378   @driver = if (match = /\Ajdbc:([^:]+)/.match(resolved_uri)) && (prok = Sequel::Database.load_adapter(match[1].to_sym, :map=>DATABASE_SETUP, :subdir=>'jdbc'))
379     prok.call(self)
380   else
381     @opts[:driver]
382   end        
383 
384   setup_type_convertor_map
385 end
cps_sync(conn) { |connection_prepared_statements ||= {}| ... } click to toggle source

Yield the native prepared statements hash for the given connection to the block in a thread-safe manner.

    # File lib/sequel/adapters/jdbc.rb
389 def cps_sync(conn, &block)
390   @connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})}
391 end
database_error_classes() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
393 def database_error_classes
394   DATABASE_ERROR_CLASSES
395 end
database_exception_sqlstate(exception, opts) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
397 def database_exception_sqlstate(exception, opts)
398   if database_exception_use_sqlstates?
399     _database_exception_sqlstate(exception, opts)
400   end
401 end
database_exception_use_sqlstates?() click to toggle source

Whether the JDBC subadapter should use SQL states for exception handling, true by default.

    # File lib/sequel/adapters/jdbc.rb
413 def database_exception_use_sqlstates?
414   true
415 end
dataset_class_default() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
417 def dataset_class_default
418   Dataset
419 end
default_fetch_size() click to toggle source

The default fetch size to use for statements. Nil by default, so that the default for the JDBC driver is used.

    # File lib/sequel/adapters/jdbc.rb
502 def default_fetch_size
503   nil
504 end
disconnect_error?(exception, opts) click to toggle source

Raise a disconnect error if the SQL state of the cause of the exception indicates so.

Calls superclass method Sequel::Database#disconnect_error?
    # File lib/sequel/adapters/jdbc.rb
422 def disconnect_error?(exception, opts)
423   super || (_database_exception_sqlstate(exception, opts) =~ /^08/)
424 end
execute_prepared_statement(name, opts=OPTS) { || ... } click to toggle source

Execute the prepared statement. If the provided name is a dataset, use that as the prepared statement, otherwise use it as a key to look it up in the prepared_statements hash. If the connection we are using has already prepared an identical statement, use that statement instead of creating another. Otherwise, prepare a new statement for the connection, bind the variables, and execute it.

    # File lib/sequel/adapters/jdbc.rb
433 def execute_prepared_statement(name, opts=OPTS)
434   args = opts[:arguments]
435   if name.is_a?(Dataset)
436     ps = name
437     name = ps.prepared_statement_name
438   else
439     ps = prepared_statement(name)
440   end
441   sql = ps.prepared_sql
442   synchronize(opts[:server]) do |conn|
443     if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql
444       cps = cps[1]
445     else
446       log_connection_yield("CLOSE #{name}", conn){cps[1].close} if cps
447       if name
448         opts = Hash[opts]
449         opts[:name] = name
450       end
451       cps = log_connection_yield("PREPARE#{" #{name}:" if name} #{sql}", conn){prepare_jdbc_statement(conn, sql, opts)}
452       if size = fetch_size
453         cps.setFetchSize(size)
454       end
455       cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
456     end
457     i = 0
458     args.each{|arg| set_ps_arg(cps, arg, i+=1)}
459     msg = "EXECUTE#{" #{name}" if name}"
460     if ps.log_sql
461       msg += " ("
462       msg << sql
463       msg << ")"
464     end
465     begin
466       if defined?(yield)
467         yield log_connection_yield(msg, conn, args){cps.executeQuery}
468       else
469         case opts[:type]
470         when :ddl
471           log_connection_yield(msg, conn, args){cps.execute}
472         when :insert
473           log_connection_yield(msg, conn, args){execute_prepared_statement_insert(cps)}
474           opts = Hash[opts]
475           opts[:prepared] = true
476           opts[:stmt] = cps
477           last_insert_id(conn, opts)
478         else
479           log_connection_yield(msg, conn, args){cps.executeUpdate}
480         end
481       end
482     rescue *DATABASE_ERROR_CLASSES => e
483       raise_error(e)
484     ensure
485       cps.close unless name
486     end
487   end
488 end
execute_prepared_statement_insert(stmt) click to toggle source

Execute the prepared insert statement

    # File lib/sequel/adapters/jdbc.rb
491 def execute_prepared_statement_insert(stmt)
492   stmt.executeUpdate
493 end
execute_statement_insert(stmt, sql) click to toggle source

Execute the insert SQL using the statement

    # File lib/sequel/adapters/jdbc.rb
496 def execute_statement_insert(stmt, sql)
497   stmt.executeUpdate(sql)
498 end
get_connection_from_jndi() click to toggle source

Gets the connection from JNDI.

    # File lib/sequel/adapters/jdbc.rb
507 def get_connection_from_jndi
508   jndi_name = JNDI_URI_REGEXP.match(uri)[1]
509   javax.naming.InitialContext.new.lookup(jndi_name).connection
510 end
get_tables(type, opts) click to toggle source

Backbone of the tables and views support.

    # File lib/sequel/adapters/jdbc.rb
521 def get_tables(type, opts)
522   ts = []
523   m = output_identifier_meth
524   if schema = opts[:schema]
525     schema = schema.to_s
526   end
527   metadata(:getTables, nil, schema, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])}
528   ts
529 end
get_uri_from_jndi() click to toggle source

Gets the JDBC connection uri from the JNDI resource.

    # File lib/sequel/adapters/jdbc.rb
513 def get_uri_from_jndi
514   conn = get_connection_from_jndi
515   conn.meta_data.url
516 ensure
517   conn.close if conn
518 end
java_sql_date(date) click to toggle source

Support Date objects used in bound variables

    # File lib/sequel/adapters/jdbc.rb
532 def java_sql_date(date)
533   java.sql.Date.new(Time.local(date.year, date.month, date.day).to_i * 1000)
534 end
java_sql_datetime(datetime) click to toggle source

Support DateTime objects used in bound variables

    # File lib/sequel/adapters/jdbc.rb
537 def java_sql_datetime(datetime)
538   ts = java.sql.Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000)
539   ts.setNanos((datetime.sec_fraction * 1000000000).to_i)
540   ts
541 end
java_sql_timestamp(time) click to toggle source

Support fractional seconds for Time objects used in bound variables

    # File lib/sequel/adapters/jdbc.rb
544 def java_sql_timestamp(time)
545   ts = java.sql.Timestamp.new(time.to_i * 1000)
546   ts.setNanos(time.nsec)
547   ts
548 end
last_insert_id(conn, opts) click to toggle source

By default, there is no support for determining the last inserted id, so return nil. This method should be overridden in subadapters.

    # File lib/sequel/adapters/jdbc.rb
557 def last_insert_id(conn, opts)
558   nil
559 end
log_connection_execute(conn, sql) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
550 def log_connection_execute(conn, sql)
551   statement(conn){|s| log_connection_yield(sql, conn){s.execute(sql)}}
552 end
metadata(*args, &block) click to toggle source

Yield the metadata for this database

    # File lib/sequel/adapters/jdbc.rb
562 def metadata(*args, &block)
563   synchronize do |c|
564     result = c.getMetaData.public_send(*args)
565     begin
566       metadata_dataset.send(:process_result_set, result, &block)
567     ensure
568       result.close
569     end
570   end
571 end
metadata_schema_and_table(table, opts) click to toggle source

Return the schema and table suitable for use with metadata queries.

    # File lib/sequel/adapters/jdbc.rb
574 def metadata_schema_and_table(table, opts)
575   im = input_identifier_meth(opts[:dataset])
576   schema, table = schema_and_table(table)
577   schema ||= opts[:schema]
578   schema = im.call(schema) if schema
579   table = im.call(table)
580   [schema, table]
581 end
prepare_jdbc_statement(conn, sql, opts) click to toggle source

Created a JDBC prepared statement on the connection with the given SQL.

    # File lib/sequel/adapters/jdbc.rb
584 def prepare_jdbc_statement(conn, sql, opts)
585   conn.prepareStatement(sql)
586 end
schema_column_set_db_type(schema) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
636 def schema_column_set_db_type(schema)
637   case schema[:type]
638   when :string
639     if schema[:db_type] =~ /\A(character( varying)?|n?(var)?char2?)\z/io && schema[:column_size] > 0
640       schema[:db_type] += "(#{schema[:column_size]})"
641     end
642   when :decimal
643     if schema[:db_type] =~ /\A(decimal|numeric)\z/io && schema[:column_size] > 0 && schema[:scale] >= 0
644       schema[:db_type] += "(#{schema[:column_size]}, #{schema[:scale]})"
645     end
646   end
647 end
schema_parse_table(table, opts=OPTS) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
649 def schema_parse_table(table, opts=OPTS)
650   m = output_identifier_meth(opts[:dataset])
651   schema, table = metadata_schema_and_table(table, opts)
652   pks, ts = [], []
653   metadata(:getPrimaryKeys, nil, schema, table) do |h|
654     next if schema_parse_table_skip?(h, schema)
655     pks << h[:column_name]
656   end
657   schemas = []
658   metadata(:getColumns, nil, schema, table, nil) do |h|
659     next if schema_parse_table_skip?(h, schema)
660     s = {
661       :type=>schema_column_type(h[:type_name]),
662       :db_type=>h[:type_name],
663       :default=>(h[:column_def] == '' ? nil : h[:column_def]),
664       :allow_null=>(h[:nullable] != 0),
665       :primary_key=>pks.include?(h[:column_name]),
666       :column_size=>h[:column_size],
667       :scale=>h[:decimal_digits],
668       :remarks=>h[:remarks]
669     }
670     if s[:primary_key]
671       s[:auto_increment] = h[:is_autoincrement] == "YES"
672     end
673     s[:max_length] = s[:column_size] if s[:type] == :string
674     if s[:db_type] =~ /number|numeric|decimal/i && s[:scale] == 0
675       s[:type] = :integer
676     end
677     schema_column_set_db_type(s)
678     schemas << h[:table_schem] unless schemas.include?(h[:table_schem])
679     ts << [m.call(h[:column_name]), s]
680   end
681   if schemas.length > 1
682     raise Error, 'Schema parsing in the jdbc adapter resulted in columns being returned for a table with the same name in multiple schemas.  Please explicitly qualify your table with a schema.'
683   end
684   ts
685 end
schema_parse_table_skip?(h, schema) click to toggle source

Skip tables in the INFORMATION_SCHEMA when parsing columns.

    # File lib/sequel/adapters/jdbc.rb
688 def schema_parse_table_skip?(h, schema)
689   h[:table_schem] == 'INFORMATION_SCHEMA'
690 end
set_ps_arg(cps, arg, i) click to toggle source

Java being java, you need to specify the type of each argument for the prepared statement, and bind it individually. This guesses which JDBC method to use, and hopefully JRuby will convert things properly for us.

    # File lib/sequel/adapters/jdbc.rb
592 def set_ps_arg(cps, arg, i)
593   case arg
594   when Integer
595     cps.setLong(i, arg)
596   when Sequel::SQL::Blob
597     cps.setBytes(i, arg.to_java_bytes)
598   when String
599     cps.setString(i, arg)
600   when Float
601     cps.setDouble(i, arg)
602   when TrueClass, FalseClass
603     cps.setBoolean(i, arg)
604   when NilClass
605     set_ps_arg_nil(cps, i)
606   when DateTime
607     cps.setTimestamp(i, java_sql_datetime(arg))
608   when Date
609     cps.setDate(i, java_sql_date(arg))
610   when Time
611     cps.setTimestamp(i, java_sql_timestamp(arg))
612   when Java::JavaSql::Timestamp
613     cps.setTimestamp(i, arg)
614   when Java::JavaSql::Date
615     cps.setDate(i, arg)
616   else
617     cps.setObject(i, arg)
618   end
619 end
set_ps_arg_nil(cps, i) click to toggle source

Use setString with a nil value by default, but this doesn’t work on all subadapters.

    # File lib/sequel/adapters/jdbc.rb
622 def set_ps_arg_nil(cps, i)
623   cps.setString(i, nil)
624 end
setup_connection(conn) click to toggle source

Return the connection. Can be overridden in subadapters for database specific setup.

    # File lib/sequel/adapters/jdbc.rb
627 def setup_connection(conn)
628   conn
629 end
setup_connection_with_opts(conn, opts) click to toggle source

Setup the connection using the given connection options. Return the connection. Can be overridden in subadapters for database specific setup.

    # File lib/sequel/adapters/jdbc.rb
632 def setup_connection_with_opts(conn, opts)
633   setup_connection(conn)
634 end
setup_type_convertor_map() click to toggle source

Called after loading subadapter-specific code, overridable by subadapters.

    # File lib/sequel/adapters/jdbc.rb
693 def setup_type_convertor_map
694 end
setup_type_convertor_map_early() click to toggle source

Called before loading subadapter-specific code, necessary so that subadapter initialization code that runs queries works correctly. This cannot be overridden in subadapters.

    # File lib/sequel/adapters/jdbc.rb
698 def setup_type_convertor_map_early
699   @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>method(:timestamp_convert))
700   @basic_type_convertor_map = TypeConvertor::BASIC_MAP.dup
701 end
statement(conn) { |stmt| ... } click to toggle source

Yield a new statement object, and ensure that it is closed before returning.

    # File lib/sequel/adapters/jdbc.rb
704 def statement(conn)
705   stmt = conn.createStatement
706   yield stmt
707 rescue *DATABASE_ERROR_CLASSES => e
708   raise_error(e)
709 ensure
710   stmt.close if stmt
711 end
timestamp_convert(r, i) click to toggle source

A conversion method for timestamp columns. This is used to make sure timestamps are converted using the correct timezone.

    # File lib/sequel/adapters/jdbc.rb
715 def timestamp_convert(r, i)
716   if v = r.getTimestamp(i)
717     to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
718   end
719 end