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

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

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

Whether or not JNDI is being used for this connection.

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

All tables in this database

    # File lib/sequel/adapters/jdbc.rb
333 def tables(opts=OPTS)
334   get_tables('TABLE', opts)
335 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
341 def uri(opts=OPTS)
342   opts = @opts.merge(opts)
343   ur = opts[:uri] || opts[:url] || opts[:database]
344   ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
345 end
views(opts=OPTS) click to toggle source

All views in this database

    # File lib/sequel/adapters/jdbc.rb
348 def views(opts=OPTS)
349   get_tables('VIEW', opts)
350 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
358 def adapter_initialize
359   @connection_prepared_statements = {}
360   @connection_prepared_statements_mutex = Mutex.new
361   @fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size
362   @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
363   raise(Error, "No connection string specified") unless uri
364   
365   resolved_uri = jndi? ? get_uri_from_jndi : uri
366   setup_type_convertor_map_early
367 
368   @driver = if (match = /\Ajdbc:([^:]+)/.match(resolved_uri)) && (prok = Sequel::Database.load_adapter(match[1].to_sym, :map=>DATABASE_SETUP, :subdir=>'jdbc'))
369     prok.call(self)
370   else
371     @opts[:driver]
372   end        
373 
374   setup_type_convertor_map
375 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
379 def cps_sync(conn, &block)
380   @connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})}
381 end
database_error_classes() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
383 def database_error_classes
384   DATABASE_ERROR_CLASSES
385 end
database_exception_sqlstate(exception, opts) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
387 def database_exception_sqlstate(exception, opts)
388   if database_exception_use_sqlstates?
389     while exception.respond_to?(:cause)
390       exception = exception.cause
391       return exception.getSQLState if exception.respond_to?(:getSQLState)
392     end
393   end
394   nil
395 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
398 def database_exception_use_sqlstates?
399   true
400 end
dataset_class_default() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
402 def dataset_class_default
403   Dataset
404 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
488 def default_fetch_size
489   nil
490 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
407 def disconnect_error?(exception, opts)
408   cause = exception.respond_to?(:cause) ? exception.cause : exception
409   super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
410 end
execute_prepared_statement(name, opts=OPTS) { |log_connection_yield(msg, conn, args){executeQuery}| ... } 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
419 def execute_prepared_statement(name, opts=OPTS)
420   args = opts[:arguments]
421   if name.is_a?(Dataset)
422     ps = name
423     name = ps.prepared_statement_name
424   else
425     ps = prepared_statement(name)
426   end
427   sql = ps.prepared_sql
428   synchronize(opts[:server]) do |conn|
429     if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql
430       cps = cps[1]
431     else
432       log_connection_yield("CLOSE #{name}", conn){cps[1].close} if cps
433       if name
434         opts = Hash[opts]
435         opts[:name] = name
436       end
437       cps = log_connection_yield("PREPARE#{" #{name}:" if name} #{sql}", conn){prepare_jdbc_statement(conn, sql, opts)}
438       if size = fetch_size
439         cps.setFetchSize(size)
440       end
441       cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
442     end
443     i = 0
444     args.each{|arg| set_ps_arg(cps, arg, i+=1)}
445     msg = "EXECUTE#{" #{name}" if name}"
446     if ps.log_sql
447       msg += " ("
448       msg << sql
449       msg << ")"
450     end
451     begin
452       if block_given?
453         yield log_connection_yield(msg, conn, args){cps.executeQuery}
454       else
455         case opts[:type]
456         when :ddl
457           log_connection_yield(msg, conn, args){cps.execute}
458         when :insert
459           log_connection_yield(msg, conn, args){execute_prepared_statement_insert(cps)}
460           opts = Hash[opts]
461           opts[:prepared] = true
462           opts[:stmt] = cps
463           last_insert_id(conn, opts)
464         else
465           log_connection_yield(msg, conn, args){cps.executeUpdate}
466         end
467       end
468     rescue *DATABASE_ERROR_CLASSES => e
469       raise_error(e)
470     ensure
471       cps.close unless name
472     end
473   end
474 end
execute_prepared_statement_insert(stmt) click to toggle source

Execute the prepared insert statement

    # File lib/sequel/adapters/jdbc.rb
477 def execute_prepared_statement_insert(stmt)
478   stmt.executeUpdate
479 end
execute_statement_insert(stmt, sql) click to toggle source

Execute the insert SQL using the statement

    # File lib/sequel/adapters/jdbc.rb
482 def execute_statement_insert(stmt, sql)
483   stmt.executeUpdate(sql)
484 end
get_connection_from_jndi() click to toggle source

Gets the connection from JNDI.

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

Backbone of the tables and views support.

    # File lib/sequel/adapters/jdbc.rb
507 def get_tables(type, opts)
508   ts = []
509   m = output_identifier_meth
510   if schema = opts[:schema]
511     schema = schema.to_s
512   end
513   metadata(:getTables, nil, schema, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])}
514   ts
515 end
get_uri_from_jndi() click to toggle source

Gets the JDBC connection uri from the JNDI resource.

    # File lib/sequel/adapters/jdbc.rb
499 def get_uri_from_jndi
500   conn = get_connection_from_jndi
501   conn.meta_data.url
502 ensure
503   conn.close if conn
504 end
java_sql_date(date) click to toggle source

Support Date objects used in bound variables

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

Support DateTime objects used in bound variables

    # File lib/sequel/adapters/jdbc.rb
523 def java_sql_datetime(datetime)
524   ts = java.sql.Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000)
525   ts.setNanos((datetime.sec_fraction * 1000000000).to_i)
526   ts
527 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
530 def java_sql_timestamp(time)
531   ts = java.sql.Timestamp.new(time.to_i * 1000)
532   ts.setNanos(time.nsec)
533   ts
534 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
543 def last_insert_id(conn, opts)
544   nil
545 end
log_connection_execute(conn, sql) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
536 def log_connection_execute(conn, sql)
537   statement(conn){|s| log_connection_yield(sql, conn){s.execute(sql)}}
538 end
metadata(*args, &block) click to toggle source

Yield the metadata for this database

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

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

    # File lib/sequel/adapters/jdbc.rb
679 def setup_type_convertor_map
680 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
684 def setup_type_convertor_map_early
685   @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>method(:timestamp_convert))
686   @basic_type_convertor_map = TypeConvertor::BASIC_MAP.dup
687 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
690 def statement(conn)
691   stmt = conn.createStatement
692   yield stmt
693 rescue *DATABASE_ERROR_CLASSES => e
694   raise_error(e)
695 ensure
696   stmt.close if stmt
697 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
701 def timestamp_convert(r, i)
702   if v = r.getTimestamp(i)
703     to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
704   end
705 end