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::JavaUtil::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 
309   metadata(:getImportedKeys, nil, schema, table) do |r|
310     next unless fk_name = r[:fk_name]
311 
312     key_seq = r[:key_seq]
313     columns = [key_seq, m.call(r[:fkcolumn_name])]
314     key = [key_seq, m.call(r[:pkcolumn_name])]
315 
316     if fk = foreign_keys[fk_name]
317       fk[:columns] << columns
318       fk[:key] << key
319     else
320       foreign_keys[fk_name] = {
321         :name=>m.call(fk_name),
322         :columns=>[columns],
323         :table=>m.call(r[:pktable_name]),
324         :key=>[key]
325       }
326     end
327   end
328 
329   fk_keys = [:columns, :key]
330   foreign_keys.values.each do |fk|
331     fk_keys.each do |k|
332       fk[k].sort!.map!{|_, v| v}
333     end
334   end
335 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
338 def indexes(table, opts=OPTS)
339   m = output_identifier_meth
340   schema, table = metadata_schema_and_table(table, opts)
341   indexes = {}
342   metadata(:getIndexInfo, nil, schema, table, false, true) do |r|
343     next unless name = r[:column_name]
344     next if respond_to?(:primary_key_index_re, true) and r[:index_name] =~ primary_key_index_re 
345     i = indexes[m.call(r[:index_name])] ||= {:columns=>[], :unique=>[false, 0].include?(r[:non_unique])}
346     i[:columns] << m.call(name)
347   end
348   indexes
349 end
jndi?() click to toggle source

Whether or not JNDI is being used for this connection.

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

All tables in this database

    # File lib/sequel/adapters/jdbc.rb
357 def tables(opts=OPTS)
358   get_tables('TABLE', opts)
359 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
365 def uri(opts=OPTS)
366   opts = @opts.merge(opts)
367   ur = opts[:uri] || opts[:url] || opts[:database]
368   ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
369 end
views(opts=OPTS) click to toggle source

All views in this database

    # File lib/sequel/adapters/jdbc.rb
372 def views(opts=OPTS)
373   get_tables('VIEW', opts)
374 end

Private Instance Methods

_database_exception_sqlstate(exception, opts) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
417 def _database_exception_sqlstate(exception, opts)
418   16.times do
419     return exception.getSQLState if exception.respond_to?(:getSQLState)
420     break unless exception.respond_to?(:cause) && (exception = exception.cause)
421   end
422 
423   nil
424 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
382 def adapter_initialize
383   @connection_prepared_statements = {}
384   @connection_prepared_statements_mutex = Mutex.new
385   @fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size
386   @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
387   raise(Error, "No connection string specified") unless uri
388   
389   resolved_uri = jndi? ? get_uri_from_jndi : uri
390   setup_type_convertor_map_early
391 
392   @driver = if (match = /\Ajdbc:([^:]+)/.match(resolved_uri)) && (prok = Sequel::Database.load_adapter(match[1].to_sym, :map=>DATABASE_SETUP, :subdir=>'jdbc'))
393     prok.call(self)
394   else
395     @opts[:driver]
396   end        
397 
398   setup_type_convertor_map
399 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
403 def cps_sync(conn, &block)
404   @connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})}
405 end
database_error_classes() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
407 def database_error_classes
408   DATABASE_ERROR_CLASSES
409 end
database_exception_sqlstate(exception, opts) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
411 def database_exception_sqlstate(exception, opts)
412   if database_exception_use_sqlstates?
413     _database_exception_sqlstate(exception, opts)
414   end
415 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
427 def database_exception_use_sqlstates?
428   true
429 end
dataset_class_default() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
431 def dataset_class_default
432   Dataset
433 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
516 def default_fetch_size
517   nil
518 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
436 def disconnect_error?(exception, opts)
437   super || (_database_exception_sqlstate(exception, opts) =~ /^08/)
438 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
447 def execute_prepared_statement(name, opts=OPTS)
448   args = opts[:arguments]
449   if name.is_a?(Dataset)
450     ps = name
451     name = ps.prepared_statement_name
452   else
453     ps = prepared_statement(name)
454   end
455   sql = ps.prepared_sql
456   synchronize(opts[:server]) do |conn|
457     if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql
458       cps = cps[1]
459     else
460       log_connection_yield("CLOSE #{name}", conn){cps[1].close} if cps
461       if name
462         opts = Hash[opts]
463         opts[:name] = name
464       end
465       cps = log_connection_yield("PREPARE#{" #{name}:" if name} #{sql}", conn){prepare_jdbc_statement(conn, sql, opts)}
466       if size = fetch_size
467         cps.setFetchSize(size)
468       end
469       cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
470     end
471     i = 0
472     args.each{|arg| set_ps_arg(cps, arg, i+=1)}
473     msg = "EXECUTE#{" #{name}" if name}"
474     if ps.log_sql
475       msg += " ("
476       msg << sql
477       msg << ")"
478     end
479     begin
480       if defined?(yield)
481         yield log_connection_yield(msg, conn, args){cps.executeQuery}
482       else
483         case opts[:type]
484         when :ddl
485           log_connection_yield(msg, conn, args){cps.execute}
486         when :insert
487           log_connection_yield(msg, conn, args){execute_prepared_statement_insert(cps)}
488           opts = Hash[opts]
489           opts[:prepared] = true
490           opts[:stmt] = cps
491           last_insert_id(conn, opts)
492         else
493           log_connection_yield(msg, conn, args){cps.executeUpdate}
494         end
495       end
496     rescue *DATABASE_ERROR_CLASSES => e
497       raise_error(e)
498     ensure
499       cps.close unless name
500     end
501   end
502 end
execute_prepared_statement_insert(stmt) click to toggle source

Execute the prepared insert statement

    # File lib/sequel/adapters/jdbc.rb
505 def execute_prepared_statement_insert(stmt)
506   stmt.executeUpdate
507 end
execute_statement_insert(stmt, sql) click to toggle source

Execute the insert SQL using the statement

    # File lib/sequel/adapters/jdbc.rb
510 def execute_statement_insert(stmt, sql)
511   stmt.executeUpdate(sql)
512 end
get_connection_from_jndi() click to toggle source

Gets the connection from JNDI.

    # File lib/sequel/adapters/jdbc.rb
521 def get_connection_from_jndi
522   jndi_name = JNDI_URI_REGEXP.match(uri)[1]
523   Java::JavaxNaming::InitialContext.new.lookup(jndi_name).connection
524 end
get_tables(type, opts) click to toggle source

Backbone of the tables and views support.

    # File lib/sequel/adapters/jdbc.rb
535 def get_tables(type, opts)
536   ts = []
537   m = output_identifier_meth
538   if schema = opts[:schema]
539     schema = schema.to_s
540   end
541   metadata(:getTables, nil, schema, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])}
542   ts
543 end
get_uri_from_jndi() click to toggle source

Gets the JDBC connection uri from the JNDI resource.

    # File lib/sequel/adapters/jdbc.rb
527 def get_uri_from_jndi
528   conn = get_connection_from_jndi
529   conn.meta_data.url
530 ensure
531   conn.close if conn
532 end
java_sql_date(date) click to toggle source

Support Date objects used in bound variables

    # File lib/sequel/adapters/jdbc.rb
546 def java_sql_date(date)
547   Java::JavaSql::Date.new(Time.local(date.year, date.month, date.day).to_i * 1000)
548 end
java_sql_datetime(datetime) click to toggle source

Support DateTime objects used in bound variables

    # File lib/sequel/adapters/jdbc.rb
551 def java_sql_datetime(datetime)
552   ts = Java::JavaSql::Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000)
553   ts.setNanos((datetime.sec_fraction * 1000000000).to_i)
554   ts
555 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
558 def java_sql_timestamp(time)
559   ts = Java::JavaSql::Timestamp.new(time.to_i * 1000)
560   ts.setNanos(time.nsec)
561   ts
562 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
571 def last_insert_id(conn, opts)
572   nil
573 end
log_connection_execute(conn, sql) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
564 def log_connection_execute(conn, sql)
565   statement(conn){|s| log_connection_yield(sql, conn){s.execute(sql)}}
566 end
metadata(*args, &block) click to toggle source

Yield the metadata for this database

    # File lib/sequel/adapters/jdbc.rb
576 def metadata(*args, &block)
577   synchronize do |c|
578     result = c.getMetaData.public_send(*args)
579     begin
580       metadata_dataset.send(:process_result_set, result, &block)
581     ensure
582       result.close
583     end
584   end
585 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
588 def metadata_schema_and_table(table, opts)
589   im = input_identifier_meth(opts[:dataset])
590   schema, table = schema_and_table(table)
591   schema ||= opts[:schema]
592   schema = im.call(schema) if schema
593   table = im.call(table)
594   [schema, table]
595 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
598 def prepare_jdbc_statement(conn, sql, opts)
599   conn.prepareStatement(sql)
600 end
schema_column_set_db_type(schema) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
650 def schema_column_set_db_type(schema)
651   case schema[:type]
652   when :string
653     if schema[:db_type] =~ /\A(character( varying)?|n?(var)?char2?)\z/i && schema[:column_size] > 0
654       schema[:db_type] += "(#{schema[:column_size]})"
655     end
656   when :decimal
657     if schema[:db_type] =~ /\A(decimal|numeric)\z/i && schema[:column_size] > 0 && schema[:scale] >= 0
658       schema[:db_type] += "(#{schema[:column_size]}, #{schema[:scale]})"
659     end
660   end
661 end
schema_parse_table(table, opts=OPTS) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
663 def schema_parse_table(table, opts=OPTS)
664   m = output_identifier_meth(opts[:dataset])
665   schema, table = metadata_schema_and_table(table, opts)
666   pks, ts = [], []
667   metadata(:getPrimaryKeys, nil, schema, table) do |h|
668     next if schema_parse_table_skip?(h, schema)
669     pks << h[:column_name]
670   end
671   schemas = []
672   metadata(:getColumns, nil, schema, table, nil) do |h|
673     next if schema_parse_table_skip?(h, schema)
674     s = {
675       :type=>schema_column_type(h[:type_name]),
676       :db_type=>h[:type_name],
677       :default=>(h[:column_def] == '' ? nil : h[:column_def]),
678       :allow_null=>(h[:nullable] != 0),
679       :primary_key=>pks.include?(h[:column_name]),
680       :column_size=>h[:column_size],
681       :scale=>h[:decimal_digits],
682       :remarks=>h[:remarks]
683     }
684     if s[:primary_key]
685       s[:auto_increment] = h[:is_autoincrement] == "YES"
686     end
687     s[:max_length] = s[:column_size] if s[:type] == :string
688     if s[:db_type] =~ /number|numeric|decimal/i && s[:scale] == 0
689       s[:type] = :integer
690     end
691     schema_column_set_db_type(s)
692     schemas << h[:table_schem] unless schemas.include?(h[:table_schem])
693     ts << [m.call(h[:column_name]), s]
694   end
695   if schemas.length > 1
696     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.'
697   end
698   ts
699 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
702 def schema_parse_table_skip?(h, schema)
703   h[:table_schem] == 'INFORMATION_SCHEMA'
704 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
606 def set_ps_arg(cps, arg, i)
607   case arg
608   when Integer
609     cps.setLong(i, arg)
610   when Sequel::SQL::Blob
611     cps.setBytes(i, arg.to_java_bytes)
612   when String
613     cps.setString(i, arg)
614   when Float
615     cps.setDouble(i, arg)
616   when TrueClass, FalseClass
617     cps.setBoolean(i, arg)
618   when NilClass
619     set_ps_arg_nil(cps, i)
620   when DateTime
621     cps.setTimestamp(i, java_sql_datetime(arg))
622   when Date
623     cps.setDate(i, java_sql_date(arg))
624   when Time
625     cps.setTimestamp(i, java_sql_timestamp(arg))
626   when Java::JavaSql::Timestamp
627     cps.setTimestamp(i, arg)
628   when Java::JavaSql::Date
629     cps.setDate(i, arg)
630   else
631     cps.setObject(i, arg)
632   end
633 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
636 def set_ps_arg_nil(cps, i)
637   cps.setString(i, nil)
638 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
641 def setup_connection(conn)
642   conn
643 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
646 def setup_connection_with_opts(conn, opts)
647   setup_connection(conn)
648 end
setup_type_convertor_map() click to toggle source

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

    # File lib/sequel/adapters/jdbc.rb
707 def setup_type_convertor_map
708 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
712 def setup_type_convertor_map_early
713   @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>method(:timestamp_convert))
714   @basic_type_convertor_map = TypeConvertor::BASIC_MAP.dup
715 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
718 def statement(conn)
719   stmt = conn.createStatement
720   yield stmt
721 rescue *DATABASE_ERROR_CLASSES => e
722   raise_error(e)
723 ensure
724   stmt.close if stmt
725 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
729 def timestamp_convert(r, i)
730   if v = r.getTimestamp(i)
731     to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
732   end
733 end