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
189 def call_sproc(name, opts = OPTS)
190   args = opts[:args] || []
191   sql = "{call #{name}(#{args.map{'?'}.join(',')})}"
192   synchronize(opts[:server]) do |conn|
193     cps = conn.prepareCall(sql)
194 
195     i = 0
196     args.each{|arg| set_ps_arg(cps, arg, i+=1)}
197 
198     begin
199       if defined?(yield)
200         yield log_connection_yield(sql, conn){cps.executeQuery}
201       else
202         log_connection_yield(sql, conn){cps.executeUpdate}
203         if opts[:type] == :insert
204           last_insert_id(conn, opts)
205         end
206       end
207     rescue *DATABASE_ERROR_CLASSES => e
208       raise_error(e)
209     ensure
210       cps.close
211     end
212   end
213 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
217 def connect(server)
218   opts = server_opts(server)
219   conn = if jndi?
220     get_connection_from_jndi
221   else
222     args = [uri(opts)]
223     args.concat([opts[:user], opts[:password]]) if opts[:user] && opts[:password]
224     begin
225       JavaSQL::DriverManager.setLoginTimeout(opts[:login_timeout]) if opts[:login_timeout]
226       raise StandardError, "skipping regular connection" if opts[:jdbc_properties]
227       JavaSQL::DriverManager.getConnection(*args)
228     rescue StandardError, *DATABASE_ERROR_CLASSES => e
229       raise e unless driver
230       # If the DriverManager can't get the connection - use the connect
231       # method of the driver. (This happens under Tomcat for instance)
232       props = java.util.Properties.new
233       if opts && opts[:user] && opts[:password]
234         props.setProperty("user", opts[:user])
235         props.setProperty("password", opts[:password])
236       end
237       opts[:jdbc_properties].each{|k,v| props.setProperty(k.to_s, v)} if opts[:jdbc_properties]
238       begin
239         c = driver.new.connect(args[0], props)
240         raise(Sequel::DatabaseError, 'driver.new.connect returned nil: probably bad JDBC connection string') unless c
241         c
242       rescue StandardError, *DATABASE_ERROR_CLASSES => e2
243         if e2.respond_to?(:message=) && e2.message != e.message
244           e2.message = "#{e2.message}\n#{e.class.name}: #{e.message}"
245         end
246         raise e2
247       end
248     end
249   end
250   setup_connection_with_opts(conn, opts)
251 end
disconnect_connection(c) click to toggle source

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

    # File lib/sequel/adapters/jdbc.rb
254 def disconnect_connection(c)
255   @connection_prepared_statements_mutex.synchronize{@connection_prepared_statements.delete(c)}
256   c.close
257 end
execute(sql, opts=OPTS) { |log_connection_yield(sql, conn){executeQuery(sql)}| ... } click to toggle source
    # File lib/sequel/adapters/jdbc.rb
259 def execute(sql, opts=OPTS, &block)
260   return call_sproc(sql, opts, &block) if opts[:sproc]
261   return execute_prepared_statement(sql, opts, &block) if [Symbol, Dataset].any?{|c| sql.is_a?(c)}
262   synchronize(opts[:server]) do |conn|
263     statement(conn) do |stmt|
264       if block
265         if size = fetch_size
266           stmt.setFetchSize(size)
267         end
268         yield log_connection_yield(sql, conn){stmt.executeQuery(sql)}
269       else
270         case opts[:type]
271         when :ddl
272           log_connection_yield(sql, conn){stmt.execute(sql)}
273         when :insert
274           log_connection_yield(sql, conn){execute_statement_insert(stmt, sql)}
275           opts = Hash[opts]
276           opts[:stmt] = stmt
277           last_insert_id(conn, opts)
278         else
279           log_connection_yield(sql, conn){stmt.executeUpdate(sql)}
280         end
281       end
282     end
283   end
284 end
Also aliased as: execute_dui
execute_ddl(sql, opts=OPTS) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
287 def execute_ddl(sql, opts=OPTS)
288   opts = Hash[opts]
289   opts[:type] = :ddl
290   execute(sql, opts)
291 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
293 def execute_insert(sql, opts=OPTS)
294   opts = Hash[opts]
295   opts[:type] = :insert
296   execute(sql, opts)
297 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
306 def foreign_key_list(table, opts=OPTS)
307   m = output_identifier_meth
308   schema, table = metadata_schema_and_table(table, opts)
309   foreign_keys = {}
310   metadata(:getImportedKeys, nil, schema, table) do |r|
311     if fk = foreign_keys[r[:fk_name]]
312       fk[:columns] << [r[:key_seq], m.call(r[:fkcolumn_name])]
313       fk[:key] << [r[:key_seq], m.call(r[:pkcolumn_name])]
314     elsif r[:fk_name]
315       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])]]}
316     end
317   end
318   foreign_keys.values.each do |fk|
319     [:columns, :key].each do |k|
320       fk[k] = fk[k].sort.map{|_, v| v}
321     end
322   end
323 end
freeze() click to toggle source
Calls superclass method Sequel::Database#freeze
    # File lib/sequel/adapters/jdbc.rb
299 def freeze
300   @type_convertor_map.freeze
301   @basic_type_convertor_map.freeze
302   super
303 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
326 def indexes(table, opts=OPTS)
327   m = output_identifier_meth
328   schema, table = metadata_schema_and_table(table, opts)
329   indexes = {}
330   metadata(:getIndexInfo, nil, schema, table, false, true) do |r|
331     next unless name = r[:column_name]
332     next if respond_to?(:primary_key_index_re, true) and r[:index_name] =~ primary_key_index_re 
333     i = indexes[m.call(r[:index_name])] ||= {:columns=>[], :unique=>[false, 0].include?(r[:non_unique])}
334     i[:columns] << m.call(name)
335   end
336   indexes
337 end
jndi?() click to toggle source

Whether or not JNDI is being used for this connection.

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

All tables in this database

    # File lib/sequel/adapters/jdbc.rb
345 def tables(opts=OPTS)
346   get_tables('TABLE', opts)
347 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
353 def uri(opts=OPTS)
354   opts = @opts.merge(opts)
355   ur = opts[:uri] || opts[:url] || opts[:database]
356   ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
357 end
views(opts=OPTS) click to toggle source

All views in this database

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

Private Instance Methods

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
370 def adapter_initialize
371   @connection_prepared_statements = {}
372   @connection_prepared_statements_mutex = Mutex.new
373   @fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size
374   @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
375   raise(Error, "No connection string specified") unless uri
376   
377   resolved_uri = jndi? ? get_uri_from_jndi : uri
378   setup_type_convertor_map_early
379 
380   @driver = if (match = /\Ajdbc:([^:]+)/.match(resolved_uri)) && (prok = Sequel::Database.load_adapter(match[1].to_sym, :map=>DATABASE_SETUP, :subdir=>'jdbc'))
381     prok.call(self)
382   else
383     @opts[:driver]
384   end        
385 
386   setup_type_convertor_map
387 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
391 def cps_sync(conn, &block)
392   @connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})}
393 end
database_error_classes() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
395 def database_error_classes
396   DATABASE_ERROR_CLASSES
397 end
database_exception_sqlstate(exception, opts) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
399 def database_exception_sqlstate(exception, opts)
400   if database_exception_use_sqlstates?
401     while exception.respond_to?(:cause)
402       exception = exception.cause
403       return exception.getSQLState if exception.respond_to?(:getSQLState)
404     end
405   end
406   nil
407 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
410 def database_exception_use_sqlstates?
411   true
412 end
dataset_class_default() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
414 def dataset_class_default
415   Dataset
416 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
500 def default_fetch_size
501   nil
502 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
419 def disconnect_error?(exception, opts)
420   cause = exception.respond_to?(:cause) ? exception.cause : exception
421   super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
422 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
431 def execute_prepared_statement(name, opts=OPTS)
432   args = opts[:arguments]
433   if name.is_a?(Dataset)
434     ps = name
435     name = ps.prepared_statement_name
436   else
437     ps = prepared_statement(name)
438   end
439   sql = ps.prepared_sql
440   synchronize(opts[:server]) do |conn|
441     if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql
442       cps = cps[1]
443     else
444       log_connection_yield("CLOSE #{name}", conn){cps[1].close} if cps
445       if name
446         opts = Hash[opts]
447         opts[:name] = name
448       end
449       cps = log_connection_yield("PREPARE#{" #{name}:" if name} #{sql}", conn){prepare_jdbc_statement(conn, sql, opts)}
450       if size = fetch_size
451         cps.setFetchSize(size)
452       end
453       cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
454     end
455     i = 0
456     args.each{|arg| set_ps_arg(cps, arg, i+=1)}
457     msg = "EXECUTE#{" #{name}" if name}"
458     if ps.log_sql
459       msg += " ("
460       msg << sql
461       msg << ")"
462     end
463     begin
464       if defined?(yield)
465         yield log_connection_yield(msg, conn, args){cps.executeQuery}
466       else
467         case opts[:type]
468         when :ddl
469           log_connection_yield(msg, conn, args){cps.execute}
470         when :insert
471           log_connection_yield(msg, conn, args){execute_prepared_statement_insert(cps)}
472           opts = Hash[opts]
473           opts[:prepared] = true
474           opts[:stmt] = cps
475           last_insert_id(conn, opts)
476         else
477           log_connection_yield(msg, conn, args){cps.executeUpdate}
478         end
479       end
480     rescue *DATABASE_ERROR_CLASSES => e
481       raise_error(e)
482     ensure
483       cps.close unless name
484     end
485   end
486 end
execute_prepared_statement_insert(stmt) click to toggle source

Execute the prepared insert statement

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

Execute the insert SQL using the statement

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

Gets the connection from JNDI.

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

Backbone of the tables and views support.

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

Gets the JDBC connection uri from the JNDI resource.

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

Support Date objects used in bound variables

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

Support DateTime objects used in bound variables

    # File lib/sequel/adapters/jdbc.rb
535 def java_sql_datetime(datetime)
536   ts = java.sql.Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000)
537   ts.setNanos((datetime.sec_fraction * 1000000000).to_i)
538   ts
539 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
542 def java_sql_timestamp(time)
543   ts = java.sql.Timestamp.new(time.to_i * 1000)
544   ts.setNanos(time.nsec)
545   ts
546 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
555 def last_insert_id(conn, opts)
556   nil
557 end
log_connection_execute(conn, sql) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
548 def log_connection_execute(conn, sql)
549   statement(conn){|s| log_connection_yield(sql, conn){s.execute(sql)}}
550 end
metadata(*args, &block) click to toggle source

Yield the metadata for this database

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

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

    # File lib/sequel/adapters/jdbc.rb
691 def setup_type_convertor_map
692 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
696 def setup_type_convertor_map_early
697   @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>method(:timestamp_convert))
698   @basic_type_convertor_map = TypeConvertor::BASIC_MAP.dup
699 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
702 def statement(conn)
703   stmt = conn.createStatement
704   yield stmt
705 rescue *DATABASE_ERROR_CLASSES => e
706   raise_error(e)
707 ensure
708   stmt.close if stmt
709 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
713 def timestamp_convert(r, i)
714   if v = r.getTimestamp(i)
715     to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
716   end
717 end