From f7c371dff8021de8e2389580bb96b0cfdca3c9ec Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 13 Sep 2007 23:21:14 +0000 Subject: [PATCH] OpenBase: update for new lib and latest Rails. Support migrations. Closes #8748. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7472 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/CHANGELOG | 2 + .../connection_adapters/openbase_adapter.rb | 346 ++++++++++++------ activerecord/test/associations/eager_test.rb | 14 +- .../test/fixtures/db_definitions/openbase.sql | 59 +-- activerecord/test/locking_test.rb | 6 +- activerecord/test/migration_test.rb | 30 +- 6 files changed, 301 insertions(+), 156 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 3fd05c498c..82095b1759 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* OpenBase: update for new lib and latest Rails. Support migrations. #8748 [dcsesq] + * Moved acts_as_tree into a plugin of the same name on the official Rails svn #9514 [lifofifo] * Moved acts_as_nested_set into a plugin of the same name on the official Rails svn #9516 [josh] diff --git a/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb b/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb index 57d16d47b4..43c7dc1ed8 100644 --- a/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb @@ -10,7 +10,6 @@ module ActiveRecord host = config[:host] username = config[:username].to_s password = config[:password].to_s - if config.has_key?(:database) database = config[:database] @@ -21,10 +20,20 @@ module ActiveRecord oba = ConnectionAdapters::OpenBaseAdapter.new( OpenBase.new(database, host, username, password), logger ) - + + if oba.raw_connection.connected? + unless oba.tables.include?(ConnectionAdapters::OpenBaseAdapter::COLUMN_SUPPORT_TABLE) + oba.execute(<<-SQL,"Creating OpenBase Column Support Table") + CREATE TABLE #{ConnectionAdapters::OpenBaseAdapter::COLUMN_SUPPORT_TABLE} (name char, type char, precision int, scale int) + SQL + end + oba.select_all("SELECT * FROM #{ConnectionAdapters::OpenBaseAdapter::COLUMN_SUPPORT_TABLE}").each do |col| + ConnectionAdapters::OpenBaseAdapter::DECIMAL_COLUMNS.store(col["name"],[col["precision"],col["scale"]]) + end + end + oba end - end module ConnectionAdapters @@ -37,8 +46,10 @@ module ActiveRecord super end end - # The OpenBase adapter works with the Ruby/Openbase driver by Tetsuya Suzuki. - # http://www.spice-of-life.net/ruby-openbase/ (needs version 0.7.3+) + + # The OpenBase adapter works with the Ruby/Openbase driver by Derrick Spell, + # provided with the distribution of OpenBase 10.0.6 and later + # http://www.openbase.com # # Options: # @@ -50,20 +61,21 @@ module ActiveRecord # The OpenBase adapter will make use of OpenBase's ability to generate unique ids # for any column with an unique index applied. Thus, if the value of a primary # key is not specified at the time an INSERT is performed, the adapter will prefetch - # a unique id for the primary key. This prefetching is also necessary in order + # a unique id for the primary key. This prefetching is also necessary in order # to return the id after an insert. # - # Caveat: Operations involving LIMIT and OFFSET do not yet work! # # Maintainer: derrick.spell@gmail.com class OpenBaseAdapter < AbstractAdapter + DECIMAL_COLUMNS = {} + COLUMN_SUPPORT_TABLE = "rails_openbase_column_support" def adapter_name 'OpenBase' end - + def native_database_types { - :primary_key => "integer UNIQUE INDEX DEFAULT _rowid", + :primary_key => "integer NOT NULL UNIQUE INDEX DEFAULT _rowid", :string => { :name => "char", :limit => 4096 }, :text => { :name => "text" }, :integer => { :name => "integer" }, @@ -77,19 +89,19 @@ module ActiveRecord :boolean => { :name => "boolean" } } end - + def supports_migrations? - false - end - + true + end + def prefetch_primary_key?(table_name = nil) true end - + def default_sequence_name(table_name, primary_key) # :nodoc: "#{table_name} #{primary_key}" end - + def next_sequence_value(sequence_name) ary = sequence_name.split(' ') if (!ary[1]) then @@ -99,66 +111,75 @@ module ActiveRecord @connection.unique_row_id(ary[0], ary[1]) end - + # QUOTING ================================================== - + def quote(value, column = nil) if value.kind_of?(String) && column && column.type == :binary "'#{@connection.insert_binary(value)}'" + elsif value.kind_of?(BigDecimal) + return "'#{value.to_s}'" + elsif column && column.type == :integer && column.sql_type =~ /decimal/ + return "'#{value.to_s}'" + elsif [Float,Fixnum,Bignum].include?(value.class) && column && column.type == :string + return "'#{value.to_s}'" else super end end - + def quoted_true "1" end - + def quoted_false "0" end - + # DATABASE STATEMENTS ====================================== def add_limit_offset!(sql, options) #:nodoc: - if limit = options[:limit] - unless offset = options[:offset] - sql << " RETURN RESULTS #{limit}" + return if options[:limit].nil? + limit = options[:limit] + offset = options[:offset] + if limit == 0 + # Mess with the where clause to ensure we get no results + if sql =~ /WHERE/i + sql.sub!(/WHERE/i, 'WHERE 1 = 2 AND ') + elsif sql =~ /ORDER\s+BY/i + sql.sub!(/ORDER\s+BY/i, 'WHERE 1 = 2 ORDER BY') else - limit = limit + offset - sql << " RETURN RESULTS #{offset} TO #{limit}" + sql << 'WHERE 1 = 2' end + elsif offset.nil? + sql << " RETURN RESULTS #{limit}" + else + sql << " RETURN RESULTS #{offset} TO #{limit + offset}" end end - - def select_all(sql, name = nil) #:nodoc: - select(sql, name) - end - - def select_one(sql, name = nil) #:nodoc: - add_limit_offset!(sql,{:limit => 1}) - results = select(sql, name) - results.first if results - end def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: execute(sql, name) update_nulls_after_insert(sql, name, pk, id_value, sequence_name) id_value end - + def execute(sql, name = nil) #:nodoc: log(sql, name) { @connection.execute(sql) } end + def direct_execute(sql, name = nil) #:nodoc: + log(sql, name) { @connection.execute(sql) } + end + def update(sql, name = nil) #:nodoc: execute(sql, name).rows_affected end alias_method :delete, :update #:nodoc: -#=begin + def begin_db_transaction #:nodoc: execute "START TRANSACTION" rescue Exception @@ -176,15 +197,13 @@ module ActiveRecord rescue Exception # Transactions aren't supported end -#=end + # SCHEMA STATEMENTS ======================================== - # Return the list of all tables in the schema search path. def tables(name = nil) #:nodoc: tables = @connection.tables tables.reject! { |t| /\A_SYS_/ === t } - tables end def columns(table_name, name = nil) #:nodoc: @@ -192,16 +211,23 @@ module ActiveRecord sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 " sql << "ORDER BY columnNumber" columns = [] - select_all(sql, name).each do |row| + direct_execute(sql, name).each_hash do |row| columns << OpenBaseColumn.new(row["fieldname"], - default_value(row["defaultvalue"]), - sql_type_name(row["typename"],row["length"]), - row["notnull"] - ) + default_value(row["defaultvalue"],row["typename"]), + sql_type_name(table_name,row["fieldname"],row["typename"],row["length"]), + row["notnull"] == 1 ? false : true) end columns end + def column_names(table_name) #:nodoc: + sql = "SELECT fieldname FROM _sys_tables " + sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 " + sql << "ORDER BY columnNumber" + names = direct_execute(sql).fetch_all + names.flatten! || names + end + def indexes(table_name, name = nil)#:nodoc: sql = "SELECT fieldname, notnull, searchindex, uniqueindex, clusteredindex FROM _sys_tables " sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 " @@ -210,109 +236,189 @@ module ActiveRecord sql << "ORDER BY columnNumber" indexes = [] execute(sql, name).each do |row| - indexes << IndexDefinition.new(table_name,index_name(row),row[3]==1,[row[0]]) + indexes << IndexDefinition.new(table_name,ob_index_name(row),row[3]==1,[row[0]]) end indexes end + def create_table(name, options = {}) #:nodoc: + return_value = super + + # Get my own copy of TableDefinition so that i can detect decimal columns + table_definition = TableDefinition.new(self) + yield table_definition + + table_definition.columns.each do |col| + if col.type == :decimal + record_decimal(name, col.name, col.precision, col.scale) + end + end + + unless options[:id] == false + primary_key = (options[:primary_key] || "id") + direct_execute("CREATE PRIMARY KEY #{name} (#{primary_key})") + end + return_value + end + + def rename_table(name, new_name) + execute "RENAME #{name} #{new_name}" + end + + def add_column(table_name, column_name, type, options = {}) + return_value = super(table_name, "COLUMN " + column_name.to_s, type, options) + if type == :decimal + record_decimal(table_name, column_name, options[:precision], options[:scale]) + end + end + + def remove_column(table_name, column_name) + execute "ALTER TABLE #{table_name} REMOVE COLUMN #{quote_column_name(column_name)}" + end + + def rename_column(table_name, column_name, new_column_name) + execute "ALTER TABLE #{table_name} RENAME #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" + end + + def add_column_options!(sql, options) #:nodoc: + sql << " NOT NULL" if options[:null] == false + sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options) + end + + def change_column(table_name, column_name, type, options = {}) #:nodoc: + unless options_include_default?(options) + options[:default] = select_one("SELECT * FROM _sys_tables WHERE tablename='#{table_name}' AND fieldname='#{column_name}'")["defaultvalue"] + end + + change_column_sql = "ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + add_column_options!(change_column_sql, options) + execute(change_column_sql) + end + + def change_column_default(table_name, column_name, default) + execute "ALTER TABLE #{table_name} COLUMN #{column_name} SET DEFAULT #{quote(default)}" + end + + def add_index(table_name, column_name, options = {}) + if Hash === options # legacy support, since this param was a string + index_type = options[:unique] ? "UNIQUE" : "" + else + index_type = options + end + execute "CREATE #{index_type} INDEX #{table_name} #{column_name}" + end + + def remove_index(table_name, options = {}) + execute "DROP INDEX #{table_name} #{options === Hash ? options[:column] : options}" + end + + def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: + return super unless type.to_s == 'decimal' + + if (scale.to_i == 2) + return 'money' + elsif (scale.to_i == 0) + return 'longlong' + else + return "char(#{precision.to_i + 1})" + end + end + private def select(sql, name = nil) - sql = translate_sql(sql) - results = execute(sql, name) + decimals = detect_decimals(sql) || [] + sql = add_order_by_rowid(sql) - date_cols = [] - col_names = [] - results.column_infos.each do |info| - col_names << info.name - date_cols << info.name if info.type == "date" + # OpenBase ignores the return results when there is a group by + # so limit the result set that we return to rails if need be + if (sql =~ /GROUP BY/i) + sql.sub!(/RETURN RESULTS (\d+)( TO (\d+))?/i,"") + + results = execute(sql, name) + if ($2) + results.fetch_offset = $1.to_i + results.fetch_limit = $3.to_i - $1.to_i + elsif ($1) + results.fetch_limit = $1.to_i + end + else + results = execute(sql, name) end - + rows = [] if ( results.rows_affected ) - results.each do |row| # loop through result rows - hashed_row = {} - row.each_index do |index| - hashed_row["#{col_names[index]}"] = row[index] unless col_names[index] == "_rowid" + results.each_hash do |row| # loop through result rows + row.delete("_rowid") if row.key?("_rowid") + decimals.each do |name, precision, scale| + row[name] = BigDecimal.new(row[name]) if row[name] === String end - date_cols.each do |name| - unless hashed_row["#{name}"].nil? or hashed_row["#{name}"].empty? - hashed_row["#{name}"] = Date.parse(hashed_row["#{name}"],false).to_s - end - end - rows << hashed_row + rows << row end end rows end - - def default_value(value) + + def default_value(value,type=nil) + return value if value.nil? + # Boolean type values return true if value =~ /true/ return false if value =~ /false/ - + # Alternative boolean default declarations + return true if (value == 1 && type == "boolean") + return false if (value == 0 && type == "boolean") + # Date / Time magic values return Time.now.to_s if value =~ /^now\(\)/i - - # Empty strings should be set to null + + # Empty strings should be set to nil return nil if value.empty? - + # Otherwise return what we got from OpenBase # and hope for the best... + # Take off the leading space and unquote + value.lstrip! + value = value[1,value.length-2] if value.first.eql?("'") && value.last.eql?("'") + return nil if value.eql?("NULL") return value - end - - def sql_type_name(type_name, length) - return "#{type_name}(#{length})" if ( type_name =~ /char/ ) - type_name end - - def index_name(row = []) + + def sql_type_name(table_name, col_name, type, length) + full_name = table_name.to_s + "." + col_name.to_s + if DECIMAL_COLUMNS.include?(full_name) && type != "longlong" + return "decimal(#{DECIMAL_COLUMNS[full_name][0]},#{DECIMAL_COLUMNS[full_name][1]})" + end + return "#{type}(#{length})" if ( type =~ /char/ ) + type + end + + def ob_index_name(row = []) name = "" name << "UNIQUE " if row[3] name << "CLUSTERED " if row[4] name << "INDEX" name end - - def translate_sql(sql) - - # Change table.* to list of columns in table - while (sql =~ /SELECT.*\s(\w+)\.\*/) - table = $1 - cols = columns(table) - if ( cols.size == 0 ) then - # Maybe this is a table alias - sql =~ /FROM(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/ - $1 =~ /[\s|,](\w+)\s+#{table}[\s|,]/ # get the tablename for this alias - cols = columns($1) + + def detect_decimals(sql) + # Detect any decimal columns that will need to be cast when fetched + decimals = [] + sql =~ /SELECT\s+(.*)\s+FROM\s+(\w+)/i + select_clause = $1 + main_table = $2 + if select_clause == "*" + column_names(main_table).each do |col| + full_name = main_table + "." + col + if DECIMAL_COLUMNS.include?(full_name) + decimals << [col,DECIMAL_COLUMNS[full_name][0].to_i,DECIMAL_COLUMNS[full_name][1].to_i] + end end - select_columns = [] - cols.each do |col| - select_columns << table + '.' + col.name - end - sql.gsub!(table + '.*',select_columns.join(", ")) if select_columns end - - # Change JOIN clause to table list and WHERE condition - while (sql =~ /JOIN/) - sql =~ /((LEFT )?(OUTER )?JOIN (\w+) ON )(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/ - join_clause = $1 + $5 - is_outer_join = $3 - join_table = $4 - join_condition = $5 - join_condition.gsub!(/=/,"*") if is_outer_join - if (sql =~ /WHERE/) - sql.gsub!(/WHERE/,"WHERE (#{join_condition}) AND") - else - sql.gsub!(join_clause,"#{join_clause} WHERE #{join_condition}") - end - sql =~ /(FROM .+?)(?:LEFT|OUTER|JOIN|WHERE|$)/ - from_clause = $1 - sql.gsub!(from_clause,"#{from_clause}, #{join_table} ") - sql.gsub!(join_clause,"") - end - + return decimals + end + + def add_order_by_rowid(sql) # ORDER BY _rowid if no explicit ORDER BY # This will ensure that find(:first) returns the first inserted row if (sql !~ /(ORDER BY)|(GROUP BY)/) @@ -322,10 +428,15 @@ module ActiveRecord sql << " ORDER BY _rowid" end end - sql end - + + def record_decimal(table_name, column_name, precision, scale) + full_name = table_name.to_s + "." + column_name.to_s + DECIMAL_COLUMNS.store(full_name, [precision.to_i,scale.to_i]) + direct_execute("INSERT INTO #{COLUMN_SUPPORT_TABLE} (name,type,precision,scale) VALUES ('#{full_name}','decimal',#{precision.to_i},#{scale.to_i})") + end + def update_nulls_after_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) sql =~ /INSERT INTO (\w+) \((.*)\) VALUES\s*\((.*)\)/m table = $1 @@ -341,9 +452,8 @@ module ActiveRecord update_cols.each { |col| update_sql << " #{col}=NULL," unless col.empty? } update_sql.chop!() update_sql << " WHERE #{pk}=#{quote(id_value)}" - execute(update_sql, name + " NULL Correction") if update_cols.size > 0 + direct_execute(update_sql,"Null Correction") if update_cols.size > 0 end - end end end diff --git a/activerecord/test/associations/eager_test.rb b/activerecord/test/associations/eager_test.rb index 019d361f3d..bbf3edd0c9 100644 --- a/activerecord/test/associations/eager_test.rb +++ b/activerecord/test/associations/eager_test.rb @@ -135,13 +135,21 @@ class EagerAssociationTest < Test::Unit::TestCase end def test_eager_with_has_many_and_limit_and_conditions - posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id") + if current_adapter?(:OpenBaseAdapter) + posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id") + else + posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id") + end assert_equal 2, posts.size assert_equal [4,5], posts.collect { |p| p.id } end def test_eager_with_has_many_and_limit_and_conditions_array - posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id") + if current_adapter?(:OpenBaseAdapter) + posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id") + else + posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id") + end assert_equal 2, posts.size assert_equal [4,5], posts.collect { |p| p.id } end @@ -399,6 +407,8 @@ class EagerAssociationTest < Test::Unit::TestCase def test_count_with_include if current_adapter?(:SQLServerAdapter, :SybaseAdapter) assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15") + elsif current_adapter?(:OpenBaseAdapter) + assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15") else assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15") end diff --git a/activerecord/test/fixtures/db_definitions/openbase.sql b/activerecord/test/fixtures/db_definitions/openbase.sql index 4f5d27e63d..a177d1688b 100644 --- a/activerecord/test/fixtures/db_definitions/openbase.sql +++ b/activerecord/test/fixtures/db_definitions/openbase.sql @@ -1,5 +1,5 @@ CREATE TABLE accounts ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, firm_id integer, credit_limit integer ) @@ -8,7 +8,7 @@ CREATE PRIMARY KEY accounts (id) go CREATE TABLE funny_jokes ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, name char(50) DEFAULT NULL ) go @@ -16,7 +16,7 @@ CREATE PRIMARY KEY funny_jokes (id) go CREATE TABLE companies ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, type char(50), ruby_type char(50), firm_id integer, @@ -37,7 +37,7 @@ CREATE TABLE developers_projects ( go CREATE TABLE developers ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, name char(100), salary integer DEFAULT 70000, created_at datetime, @@ -48,7 +48,7 @@ CREATE PRIMARY KEY developers (id) go CREATE TABLE projects ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, name char(100), type char(255) ) @@ -57,7 +57,7 @@ CREATE PRIMARY KEY projects (id) go CREATE TABLE topics ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, title char(255), author_name char(255), author_email_address char(255), @@ -75,7 +75,7 @@ CREATE PRIMARY KEY topics (id) go CREATE TABLE customers ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, name char, balance integer default 0, address_street char, @@ -88,7 +88,7 @@ CREATE PRIMARY KEY customers (id) go CREATE TABLE orders ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, name char, billing_customer_id integer, shipping_customer_id integer @@ -98,7 +98,7 @@ CREATE PRIMARY KEY orders (id) go CREATE TABLE movies ( - movieid integer UNIQUE INDEX DEFAULT _rowid, + movieid integer NOT NULL UNIQUE INDEX DEFAULT _rowid, name text ) go @@ -114,7 +114,7 @@ CREATE PRIMARY KEY subscribers (nick) go CREATE TABLE booleantests ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, value boolean ) go @@ -131,12 +131,17 @@ CREATE TABLE defaults ( fixed_time timestamp default '2004-01-01 00:00:00.000000-00', char1 char(1) default 'Y', char2 char(50) default 'a char field', - char3 text default 'a text field' + char3 text default 'a text field', + positive_integer integer default 1, + negative_integer integer default -1, + decimal_number money default 2.78 ) go +CREATE PRIMARY KEY defaults (id) +go CREATE TABLE auto_id_tests ( - auto_id integer UNIQUE INDEX DEFAULT _rowid, + auto_id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, value integer ) go @@ -144,20 +149,24 @@ CREATE PRIMARY KEY auto_id_tests (auto_id) go CREATE TABLE entrants ( - id integer UNIQUE INDEX , - name text, - course_id integer + id integer NOT NULL UNIQUE INDEX, + name text NOT NULL, + course_id integer NOT NULL ) go +CREATE PRIMARY KEY entrants (id) +go CREATE TABLE colnametests ( id integer UNIQUE INDEX , references integer NOT NULL ) go +CREATE PRIMARY KEY colnametests (id) +go CREATE TABLE mixins ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, parent_id integer, type char, pos integer, @@ -172,7 +181,7 @@ CREATE PRIMARY KEY mixins (id) go CREATE TABLE people ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, first_name text, lock_version integer default 0 ) @@ -181,7 +190,7 @@ CREATE PRIMARY KEY people (id) go CREATE TABLE readers ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, post_id integer NOT NULL, person_id integer NOT NULL ) @@ -190,7 +199,7 @@ CREATE PRIMARY KEY readers (id) go CREATE TABLE binaries ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, data object ) go @@ -228,7 +237,7 @@ CREATE TABLE authors ( go CREATE TABLE tasks ( - id integer UNIQUE INDEX DEFAULT _rowid, + id integer NOT NULL UNIQUE INDEX DEFAULT _rowid, starting datetime, ending datetime ) @@ -283,11 +292,11 @@ go CREATE TABLE numeric_data ( id INTEGER NOT NULL DEFAULT _rowid, - bank_balance DECIMAL(10,2), - big_bank_balance DECIMAL(15,2), - world_population DECIMAL(10), - my_house_population DECIMAL(2), - decimal_number_with_default DECIMAL(3,2) DEFAULT 2.78 + bank_balance MONEY, + big_bank_balance MONEY, + world_population longlong, + my_house_population longlong, + decimal_number_with_default MONEY DEFAULT 2.78 ); go CREATE PRIMARY KEY numeric_data (id) diff --git a/activerecord/test/locking_test.rb b/activerecord/test/locking_test.rb index 3194727822..5b23aab515 100644 --- a/activerecord/test/locking_test.rb +++ b/activerecord/test/locking_test.rb @@ -119,6 +119,8 @@ class OptimisticLockingTest < Test::Unit::TestCase def add_counter_column_to(model) model.connection.add_column model.table_name, :test_count, :integer, :null => false, :default => 0 model.reset_column_information + # OpenBase does not set a value to existing rows when adding a not null default column + model.update_all(:test_count => 0) if current_adapter?(:OpenBaseAdapter) end def remove_counter_column_from(model) @@ -146,9 +148,9 @@ end # blocks, so separate script called by Kernel#system is needed. # (See exec vs. async_exec in the PostgreSQL adapter.) -# TODO: The SQL Server and Sybase adapters currently have no support for pessimistic locking +# TODO: The SQL Server, Sybase, and OpenBase adapters currently have no support for pessimistic locking -unless current_adapter?(:SQLServerAdapter, :SybaseAdapter) +unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :OpenBaseAdapter) class PessimisticLockingTest < Test::Unit::TestCase self.use_transactional_fixtures = false fixtures :people, :readers diff --git a/activerecord/test/migration_test.rb b/activerecord/test/migration_test.rb index dec126bd45..2d483112d1 100644 --- a/activerecord/test/migration_test.rb +++ b/activerecord/test/migration_test.rb @@ -59,7 +59,8 @@ if ActiveRecord::Base.connection.supports_migrations? assert_nothing_raised { Person.connection.remove_index("people", "last_name") } # Orcl nds shrt indx nms. Sybs 2. - unless current_adapter?(:OracleAdapter, :SybaseAdapter) + # OpenBase does not have named indexes. You must specify a single column name + unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter) assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } @@ -72,11 +73,15 @@ if ActiveRecord::Base.connection.supports_migrations? # quoting # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word - assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) } - assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) } - + # OpenBase does not have named indexes. You must specify a single column name + unless current_adapter?(:OpenBaseAdapter) + assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) } + assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) } + end + # Sybase adapter does not support indexes on :boolean columns - unless current_adapter?(:SybaseAdapter) + # OpenBase does not have named indexes. You must specify a single column + unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter) assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") } assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") } end @@ -202,7 +207,12 @@ if ActiveRecord::Base.connection.supports_migrations? assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" } assert_raises(ActiveRecord::StatementInvalid) do - Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)" + unless current_adapter?(:OpenBaseAdapter) + Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)" + else + Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)", + "Testing Insert","id",2) + end end ensure Person.connection.drop_table :testings rescue nil @@ -221,6 +231,8 @@ if ActiveRecord::Base.connection.supports_migrations? # Do a manual insertion if current_adapter?(:OracleAdapter) Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)" + elsif current_adapter?(:OpenBaseAdapter) + Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')" else Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)" end @@ -514,8 +526,8 @@ if ActiveRecord::Base.connection.supports_migrations? assert !Reminder.table_exists? WeNeedReminders.up - - assert Reminder.create("content" => "hello world", "remind_at" => Time.now) + + assert Reminder.create("content" => "hello world", "remind_at" => Time.now) assert_equal "hello world", Reminder.find(:first).content WeNeedReminders.down @@ -773,7 +785,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert Person.column_methods_hash.include?(:last_name) assert_equal 2, ActiveRecord::Migrator.current_version end - + def test_create_table_with_custom_sequence_name return unless current_adapter? :OracleAdapter