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

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

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

Whether or not JNDI is being used for this connection.

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

All tables in this database

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

All views in this database

    # File lib/sequel/adapters/jdbc.rb
334 def views(opts=OPTS)
335   get_tables('VIEW', opts)
336 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
344 def adapter_initialize
345   @connection_prepared_statements = {}
346   @connection_prepared_statements_mutex = Mutex.new
347   @fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size
348   @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
349   raise(Error, "No connection string specified") unless uri
350   
351   resolved_uri = jndi? ? get_uri_from_jndi : uri
352   setup_type_convertor_map_early
353 
354   @driver = if (match = /\Ajdbc:([^:]+)/.match(resolved_uri)) && (prok = Sequel::Database.load_adapter(match[1].to_sym, :map=>DATABASE_SETUP, :subdir=>'jdbc'))
355     prok.call(self)
356   else
357     @opts[:driver]
358   end        
359 
360   setup_type_convertor_map
361 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
365 def cps_sync(conn, &block)
366   @connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})}
367 end
database_error_classes() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
369 def database_error_classes
370   DATABASE_ERROR_CLASSES
371 end
database_exception_sqlstate(exception, opts) click to toggle source
    # File lib/sequel/adapters/jdbc.rb
373 def database_exception_sqlstate(exception, opts)
374   if database_exception_use_sqlstates?
375     while exception.respond_to?(:cause)
376       exception = exception.cause
377       return exception.getSQLState if exception.respond_to?(:getSQLState)
378     end
379   end
380   nil
381 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
384 def database_exception_use_sqlstates?
385   true
386 end
dataset_class_default() click to toggle source
    # File lib/sequel/adapters/jdbc.rb
388 def dataset_class_default
389   Dataset
390 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
471 def default_fetch_size
472   nil
473 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
393 def disconnect_error?(exception, opts)
394   cause = exception.respond_to?(:cause) ? exception.cause : exception
395   super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
396 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
405 def execute_prepared_statement(name, opts=OPTS)
406   args = opts[:arguments]
407   if name.is_a?(Dataset)
408     ps = name
409     name = ps.prepared_statement_name
410   else
411     ps = prepared_statement(name)
412   end
413   sql = ps.prepared_sql
414   synchronize(opts[:server]) do |conn|
415     if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql
416       cps = cps[1]
417     else
418       log_connection_yield("CLOSE #{name}", conn){cps[1].close} if cps
419       if name
420         opts = Hash[opts]
421         opts[:name] = name
422       end
423       cps = log_connection_yield("PREPARE#{" #{name}:" if name} #{sql}", conn){prepare_jdbc_statement(conn, sql, opts)}
424       if size = fetch_size
425         cps.setFetchSize(size)
426       end
427       cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
428     end
429     i = 0
430     args.each{|arg| set_ps_arg(cps, arg, i+=1)}
431     msg = "EXECUTE#{" #{name}" if name}"
432     if ps.log_sql
433       msg += " ("
434       msg << sql
435       msg << ")"
436     end
437     begin
438       if block_given?
439         yield log_connection_yield(msg, conn, args){cps.executeQuery}
440       else
441         case opts[:type]
442         when :ddl
443           log_connection_yield(msg, conn, args){cps.execute}
444         when :insert
445           log_connection_yield(msg, conn, args){execute_prepared_statement_insert(cps)}
446           last_insert_id(conn, Hash[opts].merge!(:prepared=>true, :stmt=>cps))
447         else
448           log_connection_yield(msg, conn, args){cps.executeUpdate}
449         end
450       end
451     rescue *DATABASE_ERROR_CLASSES => e
452       raise_error(e)
453     ensure
454       cps.close unless name
455     end
456   end
457 end
execute_prepared_statement_insert(stmt) click to toggle source

Execute the prepared insert statement

    # File lib/sequel/adapters/jdbc.rb
460 def execute_prepared_statement_insert(stmt)
461   stmt.executeUpdate
462 end
execute_statement_insert(stmt, sql) click to toggle source

Execute the insert SQL using the statement

    # File lib/sequel/adapters/jdbc.rb
465 def execute_statement_insert(stmt, sql)
466   stmt.executeUpdate(sql)
467 end
get_connection_from_jndi() click to toggle source

Gets the connection from JNDI.

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

Backbone of the tables and views support.

    # File lib/sequel/adapters/jdbc.rb
490 def get_tables(type, opts)
491   ts = []
492   m = output_identifier_meth
493   if schema = opts[:schema]
494     schema = schema.to_s
495   end
496   metadata(:getTables, nil, schema, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])}
497   ts
498 end
get_uri_from_jndi() click to toggle source

Gets the JDBC connection uri from the JNDI resource.

    # File lib/sequel/adapters/jdbc.rb
482 def get_uri_from_jndi
483   conn = get_connection_from_jndi
484   conn.meta_data.url
485 ensure
486   conn.close if conn
487 end
java_sql_date(date) click to toggle source

Support Date objects used in bound variables

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

Support DateTime objects used in bound variables

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

Yield the metadata for this database

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

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

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