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

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

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

Whether or not JNDI is being used for this connection.

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

All tables in this database

    # File lib/sequel/adapters/jdbc.rb
327 def tables(opts=OPTS)
328   get_tables('TABLE', opts)
329 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
335 def uri(opts=OPTS)
336   opts = @opts.merge(opts)
337   ur = opts[:uri] || opts[:url] || opts[:database]
338   ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
339 end
views(opts=OPTS) click to toggle source

All views in this database

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

Execute the prepared insert statement

    # File lib/sequel/adapters/jdbc.rb
471 def execute_prepared_statement_insert(stmt)
472   stmt.executeUpdate
473 end
execute_statement_insert(stmt, sql) click to toggle source

Execute the insert SQL using the statement

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

Gets the connection from JNDI.

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

Backbone of the tables and views support.

    # File lib/sequel/adapters/jdbc.rb
501 def get_tables(type, opts)
502   ts = []
503   m = output_identifier_meth
504   if schema = opts[:schema]
505     schema = schema.to_s
506   end
507   metadata(:getTables, nil, schema, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])}
508   ts
509 end
get_uri_from_jndi() click to toggle source

Gets the JDBC connection uri from the JNDI resource.

    # File lib/sequel/adapters/jdbc.rb
493 def get_uri_from_jndi
494   conn = get_connection_from_jndi
495   conn.meta_data.url
496 ensure
497   conn.close if conn
498 end
java_sql_date(date) click to toggle source

Support Date objects used in bound variables

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

Support DateTime objects used in bound variables

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

Yield the metadata for this database

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

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

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