Permalink
Browse files

Add schema parsing support to the JDBC adapter

This commit adds support for the schema parser to the JDBC adapter
using the JDBC metadata methods.  It will be used when a JDBC
subadapter is used that doesn't have support for schema parsing in
a shared adapter.  Currently, that means that the following databases
should have schema parsing support when used with the JDBC adapter:
H2, MSSQL, and Oracle.  The schema parsing support was tested with the
H2 subadapter and it works for me.

This commit also includes an update to the H2 JDBC subadapter for
adding a primary key.  This doesn't appear to work yet (I get
"Timeout trying to lock table SYS" exception), but at least it
appears to be sending the correct SQL.  The H2 JDBC subadapter
doesn't appear to want to drop columns which are foreign key
references to other tables, but I consider that a database problem.
In total, this commit brings the number of broken integration tests
using the H2 JDBC subadapter from 12 to 4.

This commit also adds a private input_identifier method to Dataset,
similar to the existing output_identifier method.
Dataset#quote_identifier uses this now, and it is also used in
the JDBC schema parsing support.
  • Loading branch information...
1 parent 9efc8ae commit 6f32295012894b00c3778188442bf1c0337d8ce1 @jeremyevans committed Jan 27, 2009
View
@@ -1,5 +1,7 @@
=== HEAD
+* Add schema parsing support to the JDBC adapter (jeremyevans)
+
* Add per-database type translation support for schema changes, translating ruby classes to database specific types (jeremyevans)
* Add Sequel::DatabaseConnectionError, for indicating that Sequel wasn't able to connect to the database (jeremyevans)
@@ -186,9 +186,9 @@ def execute_insert(sql, opts={})
end
# All tables in this database
- def tables(server=nil)
+ def tables
ts = []
- synchronize(server){|c| dataset.send(:process_result_set, c.getMetaData.getTables(nil, nil, nil, ['TABLE'].to_java(:string))){|h| ts << dataset.send(:output_identifier, h[:table_name])}}
+ metadata(:getTables, nil, nil, nil, ['TABLE'].to_java(:string)){|h| ts << dataset.send(:output_identifier, h[:table_name])}
ts
end
@@ -231,6 +231,11 @@ def uri(opts={})
private
+ # The JDBC adapter should not need the pool to convert exceptions.
+ def connection_pool_default_options
+ super.merge(:pool_convert_exceptions=>false)
+ end
+
# Close given adapter connections
def disconnect_connection(c)
c.close
@@ -296,6 +301,11 @@ def last_insert_id(conn, opts)
nil
end
+ # Yield the metadata for this database
+ def metadata(*args, &block)
+ synchronize{|c| dataset.send(:process_result_set, c.getMetaData.send(*args), &block)}
+ 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
@@ -326,9 +336,19 @@ def setup_connection(conn)
conn
end
- # The JDBC adapter should not need the pool to convert exceptions.
- def connection_pool_default_options
- super.merge(:pool_convert_exceptions=>false)
+ # All tables in this database
+ def schema_parse_table(table, opts={})
+ schema, table = schema_and_table(table)
+ schema = dataset.send(:input_identifier, schema) if schema
+ table = dataset.send(:input_identifier, table)
+ pks, ts = [], []
+ metadata(:getPrimaryKeys, nil, schema, table) do |h|
+ pks << h[:column_name]
+ end
+ metadata(:getColumns, nil, schema, table, nil) do |h|
+ ts << [dataset.send(:output_identifier, h[:column_name]), {:type=>schema_column_type(h[:type_name]), :db_type=>h[:type_name], :default=>(h[:column_def] == '' ? nil : h[:column_def]), :allow_null=>(h[:nullable] != 0), :primary_key=>pks.include?(h[:column_name])}]
+ end
+ ts
end
end
@@ -4,6 +4,21 @@ module JDBC
module H2
# Instance methods for H2 Database objects accessed via JDBC.
module DatabaseMethods
+ # H2 needs to add a primary key column as a constraint
+ def alter_table_sql(table, op)
+ case op[:op]
+ when :add_column
+ if op.delete(:primary_key)
+ sql = super(table, op)
+ [sql, "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})"]
+ else
+ super(table, op)
+ end
+ else
+ super(table, op)
+ end
+ end
+
# Return Sequel::JDBC::H2::Dataset object with the given opts.
def dataset(opts=nil)
Sequel::JDBC::H2::Dataset.new(self, opts)
@@ -481,6 +481,12 @@ def execute_dui(sql, opts={}, &block)
def execute_insert(sql, opts={}, &block)
@db.execute_insert(sql, {:server=>@opts[:server] || :default}.merge(opts), &block)
end
+
+ # Modify the identifier returned from the database based on the
+ # identifier_output_method.
+ def input_identifier(v)
+ (i = identifier_input_method) ? v.to_s.send(i) : v.to_s
+ end
# Modify the receiver with the results of sending the meth, args, and block
# to the receiver and merging the options of the resulting dataset into
@@ -572,8 +572,7 @@ def qualified_identifier_sql(qcr)
# being quoted, returns name as a string. If identifiers are being quoted
# quote the name with quoted_identifier.
def quote_identifier(name)
- name = name.to_s
- name = name.send(@identifier_input_method) if @identifier_input_method
+ name = input_identifier(name)
name = quoted_identifier(name) if quote_identifiers?
name
end

0 comments on commit 6f32295

Please sign in to comment.