class Sequel::JDBC::Database
Attributes
Map of JDBC type ids to callable objects that return appropriate ruby or java values.
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.
The Java database driver we are using (should be a Java class)
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.
Map of JDBC type ids to callable objects that return appropriate ruby values.
Public Instance Methods
Source
# File lib/sequel/adapters/jdbc.rb 187 def call_sproc(name, opts = OPTS) 188 args = opts[:args] || [] 189 sql = "{call #{name}(#{args.map{'?'}.join(',')})}" 190 synchronize(opts[:server]) do |conn| 191 begin 192 cps = conn.prepareCall(sql) 193 194 i = 0 195 args.each{|arg| set_ps_arg(cps, arg, i+=1)} 196 197 if defined?(yield) 198 yield log_connection_yield(sql, conn){cps.executeQuery} 199 else 200 log_connection_yield(sql, conn){cps.executeUpdate} 201 if opts[:type] == :insert 202 last_insert_id(conn, opts) 203 end 204 end 205 rescue *DATABASE_ERROR_CLASSES => e 206 raise_error(e) 207 ensure 208 cps.close if cps 209 end 210 end 211 end
Execute the given stored procedure with the give name. If a block is given, the stored procedure should return rows.
Source
# File lib/sequel/adapters/jdbc.rb 215 def connect(server) 216 opts = server_opts(server) 217 conn = if jndi? 218 get_connection_from_jndi 219 else 220 args = [uri(opts)] 221 args.concat([opts[:user], opts[:password]]) if opts[:user] && opts[:password] 222 begin 223 JavaSQL::DriverManager.setLoginTimeout(opts[:login_timeout]) if opts[:login_timeout] 224 raise StandardError, "skipping regular connection" if opts[:jdbc_properties] 225 JavaSQL::DriverManager.getConnection(*args) 226 rescue StandardError, *DATABASE_ERROR_CLASSES => e 227 raise e unless driver 228 # If the DriverManager can't get the connection - use the connect 229 # method of the driver. (This happens under Tomcat for instance) 230 props = Java::JavaUtil::Properties.new 231 if opts && opts[:user] && opts[:password] 232 props.setProperty("user", opts[:user]) 233 props.setProperty("password", opts[:password]) 234 end 235 opts[:jdbc_properties].each{|k,v| props.setProperty(k.to_s, v)} if opts[:jdbc_properties] 236 begin 237 c = driver.new.connect(args[0], props) 238 raise(Sequel::DatabaseError, 'driver.new.connect returned nil: probably bad JDBC connection string') unless c 239 c 240 rescue StandardError, *DATABASE_ERROR_CLASSES => e2 241 if e2.respond_to?(:message=) && e2.message != e.message 242 e2.message = "#{e2.message}\n#{e.class.name}: #{e.message}" 243 end 244 raise e2 245 end 246 end 247 end 248 setup_connection_with_opts(conn, opts) 249 end
Connect to the database using JavaSQL::DriverManager.getConnection, and falling back to driver.new.connect if the driver is known.
Source
# File lib/sequel/adapters/jdbc.rb 252 def disconnect_connection(c) 253 @connection_prepared_statements_mutex.synchronize{@connection_prepared_statements.delete(c)} 254 c.close 255 end
Close given adapter connections, and delete any related prepared statements.
Source
# File lib/sequel/adapters/jdbc.rb 257 def execute(sql, opts=OPTS, &block) 258 return call_sproc(sql, opts, &block) if opts[:sproc] 259 return execute_prepared_statement(sql, opts, &block) if [Symbol, Dataset].any?{|c| sql.is_a?(c)} 260 synchronize(opts[:server]) do |conn| 261 statement(conn) do |stmt| 262 if block 263 if size = fetch_size 264 stmt.setFetchSize(size) 265 end 266 yield log_connection_yield(sql, conn){stmt.executeQuery(sql)} 267 else 268 case opts[:type] 269 when :ddl 270 log_connection_yield(sql, conn){stmt.execute(sql)} 271 when :insert 272 log_connection_yield(sql, conn){execute_statement_insert(stmt, sql)} 273 opts = Hash[opts] 274 opts[:stmt] = stmt 275 last_insert_id(conn, opts) 276 else 277 log_connection_yield(sql, conn){stmt.executeUpdate(sql)} 278 end 279 end 280 end 281 end 282 end
Source
# File lib/sequel/adapters/jdbc.rb 285 def execute_ddl(sql, opts=OPTS) 286 opts = Hash[opts] 287 opts[:type] = :ddl 288 execute(sql, opts) 289 end
Source
# File lib/sequel/adapters/jdbc.rb 291 def execute_insert(sql, opts=OPTS) 292 opts = Hash[opts] 293 opts[:type] = :insert 294 execute(sql, opts) 295 end
Source
# File lib/sequel/adapters/jdbc.rb 304 def foreign_key_list(table, opts=OPTS) 305 m = output_identifier_meth 306 schema, table = metadata_schema_and_table(table, opts) 307 foreign_keys = {} 308 309 metadata(:getImportedKeys, nil, schema, table) do |r| 310 next unless fk_name = r[:fk_name] 311 312 key_seq = r[:key_seq] 313 columns = [key_seq, m.call(r[:fkcolumn_name])] 314 key = [key_seq, m.call(r[:pkcolumn_name])] 315 316 if fk = foreign_keys[fk_name] 317 fk[:columns] << columns 318 fk[:key] << key 319 else 320 foreign_keys[fk_name] = { 321 :name=>m.call(fk_name), 322 :columns=>[columns], 323 :table=>m.call(r[:pktable_name]), 324 :key=>[key] 325 } 326 end 327 end 328 329 fk_keys = [:columns, :key] 330 foreign_keys.values.each do |fk| 331 fk_keys.each do |k| 332 fk[k].sort!.map!{|_, v| v} 333 end 334 end 335 end
Use the JDBC metadata to get a list of foreign keys for the table.
Source
# File lib/sequel/adapters/jdbc.rb 297 def freeze 298 @type_convertor_map.freeze 299 @basic_type_convertor_map.freeze 300 super 301 end
Sequel::Database#freeze
Source
# File lib/sequel/adapters/jdbc.rb 338 def indexes(table, opts=OPTS) 339 m = output_identifier_meth 340 schema, table = metadata_schema_and_table(table, opts) 341 indexes = {} 342 metadata(:getIndexInfo, nil, schema, table, false, true) do |r| 343 next unless name = r[:column_name] 344 next if respond_to?(:primary_key_index_re, true) and r[:index_name] =~ primary_key_index_re 345 i = indexes[m.call(r[:index_name])] ||= {:columns=>[], :unique=>[false, 0].include?(r[:non_unique])} 346 i[:columns] << m.call(name) 347 end 348 indexes 349 end
Use the JDBC metadata to get the index information for the table.
Source
# File lib/sequel/adapters/jdbc.rb 352 def jndi? 353 !!(uri =~ JNDI_URI_REGEXP) 354 end
Whether or not JNDI is being used for this connection.
Source
# File lib/sequel/adapters/jdbc.rb 357 def tables(opts=OPTS) 358 get_tables('TABLE', opts) 359 end
All tables in this database
Source
# File lib/sequel/adapters/jdbc.rb 365 def uri(opts=OPTS) 366 opts = @opts.merge(opts) 367 ur = opts[:uri] || opts[:url] || opts[:database] 368 ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}" 369 end
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 connection strings.
Source
# File lib/sequel/adapters/jdbc.rb 372 def views(opts=OPTS) 373 get_tables('VIEW', opts) 374 end
All views in this database
Private Instance Methods
Source
# File lib/sequel/adapters/jdbc.rb 417 def _database_exception_sqlstate(exception, opts) 418 16.times do 419 return exception.getSQLState if exception.respond_to?(:getSQLState) 420 break unless exception.respond_to?(:cause) && (exception = exception.cause) 421 end 422 423 nil 424 end
Source
# File lib/sequel/adapters/jdbc.rb 382 def adapter_initialize 383 @connection_prepared_statements = {} 384 @connection_prepared_statements_mutex = Mutex.new 385 @fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size 386 @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true)) 387 raise(Error, "No connection string specified") unless uri 388 389 resolved_uri = jndi? ? get_uri_from_jndi : uri 390 setup_type_convertor_map_early 391 392 @driver = if (match = /\Ajdbc:([^:]+)/.match(resolved_uri)) && (prok = Sequel::Database.load_adapter(match[1].to_sym, :map=>DATABASE_SETUP, :subdir=>'jdbc')) 393 prok.call(self) 394 else 395 @opts[:driver] 396 end 397 398 setup_type_convertor_map 399 end
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.
Source
# File lib/sequel/adapters/jdbc.rb 403 def cps_sync(conn, &block) 404 @connection_prepared_statements_mutex.synchronize{yield(@connection_prepared_statements[conn] ||= {})} 405 end
Yield the native prepared statements hash for the given connection to the block in a thread-safe manner.
Source
# File lib/sequel/adapters/jdbc.rb 407 def database_error_classes 408 DATABASE_ERROR_CLASSES 409 end
Source
# File lib/sequel/adapters/jdbc.rb 411 def database_exception_sqlstate(exception, opts) 412 if database_exception_use_sqlstates? 413 _database_exception_sqlstate(exception, opts) 414 end 415 end
Source
# File lib/sequel/adapters/jdbc.rb 427 def database_exception_use_sqlstates? 428 true 429 end
Source
# File lib/sequel/adapters/jdbc.rb 431 def dataset_class_default 432 Dataset 433 end
Source
# File lib/sequel/adapters/jdbc.rb 517 def default_fetch_size 518 nil 519 end
The default fetch size to use for statements. Nil by default, so that the default for the JDBC driver is used.
Source
# File lib/sequel/adapters/jdbc.rb 436 def disconnect_error?(exception, opts) 437 super || (_database_exception_sqlstate(exception, opts) =~ /^08/) 438 end
Raise a disconnect error if the SQL state of the cause of the exception indicates so.
Sequel::Database#disconnect_error?
Source
# File lib/sequel/adapters/jdbc.rb 447 def execute_prepared_statement(name, opts=OPTS) 448 args = opts[:arguments] 449 if name.is_a?(Dataset) 450 ps = name 451 name = ps.prepared_statement_name 452 else 453 ps = prepared_statement(name) 454 end 455 sql = ps.prepared_sql 456 synchronize(opts[:server]) do |conn| 457 if name and cps = cps_sync(conn){|cpsh| cpsh[name]} and cps[0] == sql 458 cps = cps[1] 459 else 460 log_connection_yield("CLOSE #{name}", conn){cps[1].close} if cps 461 if name 462 opts = Hash[opts] 463 opts[:name] = name 464 end 465 cps = log_connection_yield("PREPARE#{" #{name}:" if name} #{sql}", conn){prepare_jdbc_statement(conn, sql, opts)} 466 if size = fetch_size 467 cps.setFetchSize(size) 468 end 469 cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name 470 end 471 i = 0 472 args.each{|arg| set_ps_arg(cps, arg, i+=1)} 473 msg = "EXECUTE#{" #{name}" if name}" 474 if ps.log_sql 475 msg += " (" 476 msg << sql 477 msg << ")" 478 end 479 begin 480 if defined?(yield) 481 yield log_connection_yield(msg, conn, args){cps.executeQuery} 482 else 483 case opts[:type] 484 when :ddl 485 log_connection_yield(msg, conn, args){cps.execute} 486 when :insert 487 log_connection_yield(msg, conn, args){execute_prepared_statement_insert(cps)} 488 opts = Hash[opts] 489 opts[:prepared] = true 490 opts[:stmt] = cps 491 last_insert_id(conn, opts) 492 else 493 log_connection_yield(msg, conn, args){cps.executeUpdate} 494 end 495 end 496 rescue *DATABASE_ERROR_CLASSES => e 497 raise_error(e) 498 ensure 499 cps.clear_parameters 500 cps.close unless name 501 end 502 end 503 end
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.
Source
# File lib/sequel/adapters/jdbc.rb 506 def execute_prepared_statement_insert(stmt) 507 stmt.executeUpdate 508 end
Execute the prepared insert statement
Source
# File lib/sequel/adapters/jdbc.rb 511 def execute_statement_insert(stmt, sql) 512 stmt.executeUpdate(sql) 513 end
Execute the insert SQL using the statement
Source
# File lib/sequel/adapters/jdbc.rb 522 def get_connection_from_jndi 523 jndi_name = JNDI_URI_REGEXP.match(uri)[1] 524 Java::JavaxNaming::InitialContext.new.lookup(jndi_name).connection 525 end
Gets the connection from JNDI.
Source
# File lib/sequel/adapters/jdbc.rb 536 def get_tables(type, opts) 537 ts = [] 538 m = output_identifier_meth 539 if schema = opts[:schema] 540 schema = schema.to_s 541 end 542 metadata(:getTables, nil, schema, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])} 543 ts 544 end
Backbone of the tables and views support.
Source
# File lib/sequel/adapters/jdbc.rb 528 def get_uri_from_jndi 529 conn = get_connection_from_jndi 530 conn.meta_data.url 531 ensure 532 conn.close if conn 533 end
Gets the JDBC connection uri from the JNDI resource.
Source
# File lib/sequel/adapters/jdbc.rb 547 def java_sql_date(date) 548 Java::JavaSql::Date.new(Time.local(date.year, date.month, date.day).to_i * 1000) 549 end
Support Date objects used in bound variables
Source
# File lib/sequel/adapters/jdbc.rb 552 def java_sql_datetime(datetime) 553 ts = Java::JavaSql::Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000) 554 ts.setNanos((datetime.sec_fraction * 1000000000).to_i) 555 ts 556 end
Support DateTime objects used in bound variables
Source
# File lib/sequel/adapters/jdbc.rb 559 def java_sql_timestamp(time) 560 ts = Java::JavaSql::Timestamp.new(time.to_i * 1000) 561 ts.setNanos(time.nsec) 562 ts 563 end
Support fractional seconds for Time objects used in bound variables
Source
# File lib/sequel/adapters/jdbc.rb 572 def last_insert_id(conn, opts) 573 nil 574 end
By default, there is no support for determining the last inserted id, so return nil. This method should be overridden in subadapters.
Source
# File lib/sequel/adapters/jdbc.rb 565 def log_connection_execute(conn, sql) 566 statement(conn){|s| log_connection_yield(sql, conn){s.execute(sql)}} 567 end
Source
# File lib/sequel/adapters/jdbc.rb 577 def metadata(*args, &block) 578 synchronize do |c| 579 result = c.getMetaData.public_send(*args) 580 begin 581 metadata_dataset.send(:process_result_set, result, &block) 582 ensure 583 result.close 584 end 585 end 586 end
Yield the metadata for this database
Source
# File lib/sequel/adapters/jdbc.rb 589 def metadata_schema_and_table(table, opts) 590 im = input_identifier_meth(opts[:dataset]) 591 schema, table = schema_and_table(table) 592 schema ||= opts[:schema] 593 schema = im.call(schema) if schema 594 table = im.call(table) 595 [schema, table] 596 end
Return the schema and table suitable for use with metadata queries.
Source
# File lib/sequel/adapters/jdbc.rb 599 def prepare_jdbc_statement(conn, sql, opts) 600 conn.prepareStatement(sql) 601 end
Source
# File lib/sequel/adapters/jdbc.rb 651 def schema_column_set_db_type(schema) 652 case schema[:type] 653 when :string 654 if schema[:db_type] =~ /\A(character( varying)?|n?(var)?char2?)\z/i && schema[:column_size] > 0 655 schema[:db_type] += "(#{schema[:column_size]})" 656 end 657 when :decimal 658 if schema[:db_type] =~ /\A(decimal|numeric)\z/i && schema[:column_size] > 0 && schema[:scale] >= 0 659 schema[:db_type] += "(#{schema[:column_size]}, #{schema[:scale]})" 660 end 661 end 662 end
Source
# File lib/sequel/adapters/jdbc.rb 664 def schema_parse_table(table, opts=OPTS) 665 m = output_identifier_meth(opts[:dataset]) 666 schema, table = metadata_schema_and_table(table, opts) 667 pks, ts = [], [] 668 metadata(:getPrimaryKeys, nil, schema, table) do |h| 669 next if schema_parse_table_skip?(h, schema) 670 pks << h[:column_name] 671 end 672 schemas = [] 673 metadata(:getColumns, nil, schema, table, nil) do |h| 674 next if schema_parse_table_skip?(h, schema) 675 s = { 676 :type=>schema_column_type(h[:type_name]), 677 :db_type=>h[:type_name], 678 :default=>(h[:column_def] == '' ? nil : h[:column_def]), 679 :allow_null=>(h[:nullable] != 0), 680 :primary_key=>pks.include?(h[:column_name]), 681 :column_size=>h[:column_size], 682 :scale=>h[:decimal_digits], 683 :remarks=>h[:remarks] 684 } 685 if s[:primary_key] 686 s[:auto_increment] = h[:is_autoincrement] == "YES" 687 end 688 s[:max_length] = s[:column_size] if s[:type] == :string 689 if s[:db_type] =~ /number|numeric|decimal/i && s[:scale] == 0 690 s[:type] = :integer 691 end 692 schema_column_set_db_type(s) 693 schemas << h[:table_schem] unless schemas.include?(h[:table_schem]) 694 ts << [m.call(h[:column_name]), s] 695 end 696 if schemas.length > 1 697 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.' 698 end 699 ts 700 end
Source
# File lib/sequel/adapters/jdbc.rb 703 def schema_parse_table_skip?(h, schema) 704 h[:table_schem] == 'INFORMATION_SCHEMA' 705 end
Skip tables in the INFORMATION_SCHEMA when parsing columns.
Source
# File lib/sequel/adapters/jdbc.rb 607 def set_ps_arg(cps, arg, i) 608 case arg 609 when Integer 610 cps.setLong(i, arg) 611 when Sequel::SQL::Blob 612 cps.setBytes(i, arg.to_java_bytes) 613 when String 614 cps.setString(i, arg) 615 when Float 616 cps.setDouble(i, arg) 617 when TrueClass, FalseClass 618 cps.setBoolean(i, arg) 619 when NilClass 620 set_ps_arg_nil(cps, i) 621 when DateTime 622 cps.setTimestamp(i, java_sql_datetime(arg)) 623 when Date 624 cps.setDate(i, java_sql_date(arg)) 625 when Time 626 cps.setTimestamp(i, java_sql_timestamp(arg)) 627 when Java::JavaSql::Timestamp 628 cps.setTimestamp(i, arg) 629 when Java::JavaSql::Date 630 cps.setDate(i, arg) 631 else 632 cps.setObject(i, arg) 633 end 634 end
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.
Source
# File lib/sequel/adapters/jdbc.rb 637 def set_ps_arg_nil(cps, i) 638 cps.setString(i, nil) 639 end
Use setString with a nil value by default, but this doesnβt work on all subadapters.
Source
# File lib/sequel/adapters/jdbc.rb 642 def setup_connection(conn) 643 conn 644 end
Return the connection. Can be overridden in subadapters for database specific setup.
Source
# File lib/sequel/adapters/jdbc.rb 647 def setup_connection_with_opts(conn, opts) 648 setup_connection(conn) 649 end
Setup the connection using the given connection options. Return the connection. Can be overridden in subadapters for database specific setup.
Source
# File lib/sequel/adapters/jdbc.rb 708 def setup_type_convertor_map 709 end
Called after loading subadapter-specific code, overridable by subadapters.
Source
# File lib/sequel/adapters/jdbc.rb 713 def setup_type_convertor_map_early 714 @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>method(:timestamp_convert)) 715 @basic_type_convertor_map = TypeConvertor::BASIC_MAP.dup 716 end
Called before loading subadapter-specific code, necessary so that subadapter initialization code that runs queries works correctly. This cannot be overridden in subadapters.
Source
# File lib/sequel/adapters/jdbc.rb 719 def statement(conn) 720 stmt = conn.createStatement 721 yield stmt 722 rescue *DATABASE_ERROR_CLASSES => e 723 raise_error(e) 724 ensure 725 stmt.close if stmt 726 end
Yield a new statement object, and ensure that it is closed before returning.
Source
# File lib/sequel/adapters/jdbc.rb 730 def timestamp_convert(r, i) 731 if v = r.getTimestamp(i) 732 to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos]) 733 end 734 end
A conversion method for timestamp columns. This is used to make sure timestamps are converted using the correct timezone.