Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #4093: FrontBaseAdapter.3.patch

File FrontBaseAdapter.3.patch, 58.9 kB (added by mlaster@metavillage.com, 3 years ago)
  • activerecord/test/connections/native_frontbase/connection.rb

    old new  
     1print "Using native Frontbase\n" 
     2require_dependency 'fixtures/course' 
     3require 'logger' 
     4 
     5ActiveRecord::Base.logger = Logger.new("debug.log") 
     6 
     7db1 = 'activerecord_unittest' 
     8db2 = 'activerecord_unittest2' 
     9 
     10ActiveRecord::Base.establish_connection( 
     11  :adapter      => "frontbase", 
     12  :host         => "localhost", 
     13  :username     => "rails", 
     14  :password     => "", 
     15  :database     => db1, 
     16  :session_name => "unittest-#{$$}" 
     17) 
     18 
     19Course.establish_connection( 
     20  :adapter      => "frontbase", 
     21  :host         => "localhost", 
     22  :username     => "rails", 
     23  :password     => "", 
     24  :database     => db2, 
     25  :session_name => "unittest-#{$$}" 
     26) 
  • activerecord/test/reflection_test.rb

    old new  
    33require 'fixtures/customer' 
    44require 'fixtures/company' 
    55require 'fixtures/company_in_module' 
     6require 'fixtures/subscriber' 
    67 
    78class ReflectionTest < Test::Unit::TestCase 
    8   fixtures :topics, :customers, :companies 
     9  fixtures :topics, :customers, :companies, :subscribers 
    910 
    1011  def setup 
    1112    @first = Topic.find(1) 
     
    3839    assert_equal :string, @first.column_for_attribute("title").type 
    3940    assert_equal 255, @first.column_for_attribute("title").limit 
    4041  end 
     42   
     43  def test_column_null_not_null 
     44    subscriber = Subscriber.find(:first) 
     45    assert subscriber.column_for_attribute("name").null 
     46    assert !subscriber.column_for_attribute("nick").null 
     47  end 
    4148 
    4249  def test_human_name_for_column 
    4350    assert_equal "Author name", @first.column_for_attribute("author_name").human_name 
  • activerecord/test/fixtures_test.rb

    old new  
    175175 
    176176 
    177177  def test_yml_file_in_subdirectory 
     178#     puts "cat.name : [#{categories(:sub_special_1).name}]" 
     179#     puts "cat.class: [#{categories(:sub_special_1).class}]" 
     180#     breakpoint 
    178181    assert_equal(categories(:sub_special_1).name, "A special category in a subdir file") 
    179182    assert_equal(categories(:sub_special_1).class, SpecialCategory) 
    180183  end 
  • activerecord/test/base_test.rb

    old new  
    10771077    end 
    10781078    assert_equal res, res3 
    10791079     
    1080     res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments c WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id" 
     1080    res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id" 
    10811081    res5 = nil 
    10821082    assert_nothing_raised do 
    1083       res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id", 
    1084                         :joins => "p, comments c", 
     1083      res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id", 
     1084                        :joins => "p, comments co", 
    10851085                        :select => "p.id") 
    10861086    end 
    10871087 
    10881088    assert_equal res4, res5  
    10891089     
    1090     res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments c WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id" 
     1090    res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id" 
    10911091    res7 = nil 
    10921092    assert_nothing_raised do 
    1093       res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id", 
    1094                         :joins => "p, comments c", 
     1093      res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id", 
     1094                        :joins => "p, comments co", 
    10951095                        :select => "p.id", 
    10961096                        :distinct => true) 
    10971097    end 
  • activerecord/test/adapter_test.rb

    old new  
    4141  if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!) 
    4242    require 'fixtures/movie' 
    4343    require 'fixtures/subscriber' 
     44     
    4445    def test_reset_empty_table_with_custom_pk 
    4546      Movie.delete_all 
    4647      Movie.connection.reset_pk_sequence! 'movies' 
    4748      assert_equal 1, Movie.create(:name => 'fight club').id 
    4849    end 
    4950 
    50     def test_reset_table_with_non_integer_pk 
    51       Subscriber.delete_all 
    52       Subscriber.connection.reset_pk_sequence! 'subscribers' 
    53  
    54       sub = Subscriber.new(:name => 'robert drake') 
    55       sub.id = 'bob drake' 
    56       assert_nothing_raised { sub.save! } 
     51    if ActiveRecord::Base.connection.adapter_name != "FrontBase" 
     52      def test_reset_table_with_non_integer_pk 
     53        Subscriber.delete_all 
     54        Subscriber.connection.reset_pk_sequence! 'subscribers' 
     55        sub = Subscriber.new(:name => 'robert drake') 
     56        sub.id = 'bob drake' 
     57        assert_nothing_raised { sub.save! } 
     58      end 
    5759    end 
    5860  end 
    5961end 
  • activerecord/test/threaded_connections_test.rb

    old new  
    11require 'abstract_unit' 
    22require 'fixtures/topic' 
    33 
    4 class ThreadedConnectionsTest < Test::Unit::TestCase 
    5   self.use_transactional_fixtures = false 
     4unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name 
     5  class ThreadedConnectionsTest < Test::Unit::TestCase 
     6    self.use_transactional_fixtures = false 
    67 
    7   fixtures :topics 
     8    fixtures :topics 
    89 
    910  def setup 
    1011    @connection = ActiveRecord::Base.remove_connection 
     
    2526    ActiveRecord::Base.allow_concurrency = use_threaded_connections 
    2627    ActiveRecord::Base.establish_connection(@connection) 
    2728     
    28     5.times do 
    29       Thread.new do 
    30         Topic.find :first 
    31         @connections << ActiveRecord::Base.active_connections.values.first 
    32       end.join 
     29      5.times do 
     30        Thread.new do 
     31          Topic.find :first 
     32          @connections << ActiveRecord::Base.active_connections.values.first 
     33        end.join 
     34      end 
    3335    end 
    34   end 
    3536 
    36   def test_threaded_connections 
    37     gather_connections(true) 
    38     assert_equal @connections.uniq.length, 5 
    39   end 
     37    def test_threaded_connections 
     38      gather_connections(true) 
     39      assert_equal @connections.uniq.length, 5 
     40    end 
    4041 
    41   def test_unthreaded_connections 
    42     gather_connections(false) 
    43     assert_equal @connections.uniq.length, 1 
     42    def test_unthreaded_connections 
     43      gather_connections(false) 
     44      assert_equal @connections.uniq.length, 1 
     45    end 
    4446  end 
    4547end 
  • activerecord/test/deprecated_finder_test.rb

    old new  
    11require 'abstract_unit' 
    22require 'fixtures/company' 
    33require 'fixtures/topic' 
     4require 'fixtures/reply' 
    45require 'fixtures/entrant' 
    56require 'fixtures/developer' 
    67 
  • activerecord/test/fixtures/db_definitions/frontbase.sql

    old new  
     1CREATE TABLE accounts ( 
     2    id integer DEFAULT unique, 
     3    firm_id integer, 
     4    credit_limit integer, 
     5    PRIMARY KEY (id) 
     6); 
     7SET UNIQUE FOR accounts(id); 
     8 
     9CREATE TABLE funny_jokes ( 
     10  id integer DEFAULT unique, 
     11  firm_id integer default NULL, 
     12  name character varying(50), 
     13  PRIMARY KEY (id) 
     14); 
     15SET UNIQUE FOR funny_jokes(id); 
     16   
     17CREATE TABLE companies ( 
     18  id integer DEFAULT unique, 
     19    "type" character varying(50), 
     20    "ruby_type" character varying(50), 
     21    firm_id integer, 
     22    name character varying(50), 
     23    client_of integer, 
     24    rating integer default 1, 
     25    PRIMARY KEY (id) 
     26); 
     27SET UNIQUE FOR companies(id); 
     28 
     29CREATE TABLE topics ( 
     30  id integer DEFAULT unique, 
     31    title character varying(255), 
     32    author_name character varying(255), 
     33    author_email_address character varying(255), 
     34    written_on timestamp, 
     35    bonus_time time, 
     36    last_read date, 
     37    content varchar(65536), 
     38    approved boolean default true, 
     39    replies_count integer default 0, 
     40    parent_id integer, 
     41    "type" character varying(50), 
     42    PRIMARY KEY (id) 
     43); 
     44SET UNIQUE FOR topics(id); 
     45 
     46CREATE TABLE developers ( 
     47  id integer DEFAULT unique, 
     48    name character varying(100), 
     49    salary integer DEFAULT 70000, 
     50    created_at timestamp, 
     51    updated_at timestamp, 
     52    PRIMARY KEY (id) 
     53); 
     54SET UNIQUE FOR developers(id); 
     55 
     56CREATE TABLE projects ( 
     57  id integer DEFAULT unique, 
     58    name character varying(100), 
     59    type varchar(255), 
     60    PRIMARY KEY (id) 
     61); 
     62SET UNIQUE FOR projects(id); 
     63 
     64CREATE TABLE developers_projects ( 
     65    developer_id integer NOT NULL, 
     66    project_id integer NOT NULL, 
     67    joined_on date, 
     68    access_level integer default 1 
     69); 
     70 
     71CREATE TABLE orders ( 
     72  id integer DEFAULT unique, 
     73    name character varying(100), 
     74    billing_customer_id integer, 
     75    shipping_customer_id integer, 
     76    PRIMARY KEY (id) 
     77); 
     78SET UNIQUE FOR orders(id); 
     79 
     80CREATE TABLE customers ( 
     81  id integer DEFAULT unique, 
     82    name character varying(100), 
     83    balance integer default 0, 
     84    address_street character varying(100), 
     85    address_city character varying(100), 
     86    address_country character varying(100), 
     87    gps_location character varying(100), 
     88    PRIMARY KEY (id) 
     89); 
     90SET UNIQUE FOR customers(id); 
     91 
     92CREATE TABLE movies ( 
     93    movieid integer DEFAULT unique, 
     94    name varchar(65536), 
     95    PRIMARY KEY (movieid) 
     96); 
     97SET UNIQUE FOR movies(movieid); 
     98 
     99CREATE TABLE subscribers ( 
     100    nick varchar(65536) NOT NULL, 
     101    name varchar(65536), 
     102    PRIMARY KEY (nick) 
     103); 
     104 
     105CREATE TABLE booleantests ( 
     106  id integer DEFAULT unique, 
     107    value boolean, 
     108    PRIMARY KEY (id) 
     109); 
     110SET UNIQUE FOR booleantests(id); 
     111 
     112CREATE TABLE auto_id_tests ( 
     113  auto_id integer DEFAULT unique, 
     114    value integer, 
     115    PRIMARY KEY (auto_id) 
     116); 
     117SET UNIQUE FOR auto_id_tests(auto_id); 
     118 
     119CREATE TABLE entrants ( 
     120  id integer DEFAULT unique, 
     121  name varchar(65536), 
     122  course_id integer, 
     123  PRIMARY KEY (id) 
     124); 
     125SET UNIQUE FOR entrants(id); 
     126 
     127CREATE TABLE colnametests ( 
     128  id integer DEFAULT unique, 
     129  "references" integer NOT NULL, 
     130  PRIMARY KEY (id) 
     131); 
     132SET UNIQUE FOR colnametests(id); 
     133 
     134CREATE TABLE mixins ( 
     135  id integer DEFAULT unique, 
     136  parent_id integer, 
     137  type character varying(100),   
     138  pos integer, 
     139  lft integer, 
     140  rgt integer, 
     141  root_id integer,   
     142  created_at timestamp, 
     143  updated_at timestamp, 
     144  PRIMARY KEY (id) 
     145); 
     146SET UNIQUE FOR mixins(id); 
     147 
     148CREATE TABLE people ( 
     149  id integer DEFAULT unique, 
     150  first_name varchar(65536), 
     151  lock_version integer default 0, 
     152  PRIMARY KEY  (id) 
     153); 
     154SET UNIQUE FOR people(id); 
     155 
     156CREATE TABLE readers ( 
     157  id integer DEFAULT unique, 
     158  post_id INTEGER NOT NULL, 
     159  person_id INTEGER NOT NULL, 
     160  PRIMARY KEY  (id) 
     161); 
     162SET UNIQUE FOR readers(id); 
     163 
     164CREATE TABLE binaries (  
     165  id integer DEFAULT unique, 
     166  data BYTE VARYING(65536), 
     167  PRIMARY KEY (id) 
     168); 
     169SET UNIQUE FOR binaries(id); 
     170 
     171CREATE TABLE computers ( 
     172  id integer DEFAULT unique, 
     173  developer integer NOT NULL, 
     174  "extendedWarranty" integer NOT NULL, 
     175  PRIMARY KEY (id) 
     176); 
     177SET UNIQUE FOR computers(id); 
     178 
     179CREATE TABLE posts ( 
     180  id integer DEFAULT unique, 
     181  author_id integer, 
     182  title varchar(255), 
     183  type varchar(255), 
     184  body varchar(65536), 
     185  PRIMARY KEY (id) 
     186); 
     187SET UNIQUE FOR posts(id); 
     188 
     189CREATE TABLE comments ( 
     190  id integer DEFAULT unique, 
     191  post_id integer, 
     192  type varchar(255), 
     193  body varchar(65536), 
     194  PRIMARY KEY (id) 
     195); 
     196SET UNIQUE FOR comments(id); 
     197 
     198CREATE TABLE authors ( 
     199  id integer DEFAULT unique, 
     200  name varchar(255) default NULL, 
     201  PRIMARY KEY (id) 
     202); 
     203SET UNIQUE FOR authors(id); 
     204 
     205CREATE TABLE tasks ( 
     206  id integer DEFAULT unique, 
     207  starting timestamp, 
     208  ending timestamp, 
     209  PRIMARY KEY (id) 
     210); 
     211SET UNIQUE FOR tasks(id); 
     212 
     213CREATE TABLE categories ( 
     214  id integer DEFAULT unique, 
     215  name varchar(255), 
     216  type varchar(255), 
     217  PRIMARY KEY (id) 
     218); 
     219SET UNIQUE FOR categories(id); 
     220 
     221CREATE TABLE categories_posts ( 
     222  category_id integer NOT NULL, 
     223  post_id integer NOT NULL 
     224); 
     225 
     226CREATE TABLE fk_test_has_pk ( 
     227  id INTEGER NOT NULL PRIMARY KEY 
     228); 
     229SET UNIQUE FOR fk_test_has_pk(id); 
     230 
     231CREATE TABLE fk_test_has_fk ( 
     232  id    INTEGER NOT NULL PRIMARY KEY, 
     233  fk_id INTEGER NOT NULL REFERENCES fk_test_has_fk(id) 
     234); 
     235SET UNIQUE FOR fk_test_has_fk(id); 
     236 
     237CREATE TABLE keyboards ( 
     238  key_number integer DEFAULT unique, 
     239  "name" character varying(50), 
     240  PRIMARY KEY (key_number) 
     241); 
     242SET UNIQUE FOR keyboards(key_number); 
     243 
     244create table "legacy_things" 
     245( 
     246  "id" int, 
     247  "tps_report_number" int default NULL, 
     248  "version" int default 0 not null, 
     249  primary key ("id") 
     250); 
     251SET UNIQUE FOR legacy_things(id); 
  • activerecord/test/fixtures/db_definitions/frontbase.drop.sql

    old new  
     1DROP TABLE accounts CASCADE; 
     2DROP TABLE funny_jokes CASCADE; 
     3DROP TABLE companies CASCADE; 
     4DROP TABLE topics CASCADE; 
     5DROP TABLE developers CASCADE; 
     6DROP TABLE projects CASCADE; 
     7DROP TABLE developers_projects CASCADE; 
     8DROP TABLE orders CASCADE; 
     9DROP TABLE customers CASCADE; 
     10DROP TABLE movies CASCADE; 
     11DROP TABLE subscribers CASCADE; 
     12DROP TABLE booleantests CASCADE; 
     13DROP TABLE auto_id_tests CASCADE; 
     14DROP TABLE entrants CASCADE; 
     15DROP TABLE colnametests CASCADE; 
     16DROP TABLE mixins CASCADE; 
     17DROP TABLE people CASCADE; 
     18DROP TABLE readers CASCADE; 
     19DROP TABLE binaries CASCADE; 
     20DROP TABLE computers CASCADE; 
     21DROP TABLE posts CASCADE; 
     22DROP TABLE comments CASCADE; 
     23DROP TABLE authors CASCADE; 
     24DROP TABLE tasks CASCADE; 
     25DROP TABLE categories CASCADE; 
     26DROP TABLE categories_posts CASCADE; 
     27DROP TABLE fk_test_has_fk CASCADE; 
     28DROP TABLE fk_test_has_pk CASCADE; 
     29DROP TABLE keyboards CASCADE; 
     30DROP TABLE legacy_things CASCADE; 
  • activerecord/test/fixtures/db_definitions/frontbase2.sql

    old new  
     1CREATE TABLE courses ( 
     2  id integer DEFAULT unique, 
     3  name varchar(100) 
     4); 
  • activerecord/test/fixtures/db_definitions/frontbase2.drop.sql

    old new  
  • activerecord/test/transactions_test.rb

    old new  
    11require 'abstract_unit' 
    22require 'fixtures/topic' 
     3require 'fixtures/reply' 
    34require 'fixtures/developer' 
    45 
    56class TransactionTest < Test::Unit::TestCase 
  • activerecord/test/pk_test.rb

    old new  
    11require "#{File.dirname(__FILE__)}/abstract_unit" 
    22require 'fixtures/topic' 
     3require 'fixtures/reply' 
    34require 'fixtures/subscriber' 
    45require 'fixtures/movie' 
    56require 'fixtures/keyboard' 
  • activerecord/Rakefile

    old new  
    2727 
    2828# Run the unit tests 
    2929 
    30 for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase
     30for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase frontbase
    3131  Rake::TestTask.new("test_#{adapter}") { |t| 
    3232    t.libs << "test" << "test/connections/native_#{adapter}" 
    3333    t.pattern = "test/*_test{,_#{adapter}}.rb" 
     
    5959  %x( createdb activerecord_unittest ) 
    6060  %x( createdb activerecord_unittest2 ) 
    6161  %x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} ) 
    62   %x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} ) 
     62  %x( psql activerecord_unittest2 -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} ) 
    6363end 
    6464 
    6565desc 'Drop the PostgreSQL test databases' 
  • activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb

    old new  
     1require 'active_record/connection_adapters/abstract_adapter' 
     2require 'frontbase' 
     3 
     4module ActiveRecord 
     5     
     6  class Base 
     7 
     8    # FrontBase only supports one unnamed sequence per table 
     9    def self.set_sequence_name( value=nil, &block ) 
     10    end 
     11     
     12    # Establishes a connection to the database that's used by all Active Record objects. 
     13    def self.frontbase_connection(config) # :nodoc: 
     14      config = config.symbolize_keys 
     15      database     = config[:database] 
     16      port         = config[:port] 
     17      host         = config[:host] 
     18      username     = config[:username] 
     19      password     = config[:password] 
     20      dbpassword   = config[:dbpassword] 
     21      session_name = config[:session_name] 
     22 
     23      if dbpassword == nil 
     24        dbpassword = "" 
     25      end 
     26       
     27      # Turn off colorization since it make tail/less output difficult 
     28      self.colorize_logging = false; 
     29       
     30       
     31      connection = FBSQL_Connect.connect(host, port, database, username, password, dbpassword,session_name) 
     32      ConnectionAdapters::FrontBaseAdapter.new(connection, logger, [host, port, database, username, password, dbpassword, session_name], config) 
     33    end         
     34  end 
     35   
     36  module ConnectionAdapters 
     37 
     38    class FrontBaseColumn < Column #:nodoc: 
     39      attr_reader :fb_autogen 
     40       
     41      def initialize(base, name, type, typename, limit, precision, scale, default, nullable) 
     42         
     43        @base, @name = base, name 
     44        @type = simplified_type(type,typename,limit) 
     45        @limit = limit 
     46        @precision, @scale = precision, scale 
     47        @default = default 
     48        @null = nullable == "YES" ? true : false 
     49        @text    = [:string, :text].include? @type 
     50        @number  = [:float, :integer].include? @type 
     51        @fb_autogen = false 
     52        if @default 
     53          if @type == :boolean then 
     54            @default.downcase! 
     55          end 
     56          @default.gsub!(/^'(.*)'$/,'\1') if @text 
     57          @fb_autogen =  @default.include?("SELECT UNIQUE FROM") 
     58          @default = type_cast(@default) 
     59        end 
     60      end 
     61       
     62      def self.hex_encode(value) 
     63        retvalue = "" 
     64        value.each_byte do |b| 
     65          retvalue << sprintf("%02X", b) 
     66        end 
     67        retvalue 
     68      end 
     69 
     70      def self.hex_decode(value) 
     71        retvalue = "" 
     72        value.scan(/../) do |h| 
     73          c = h.hex 
     74          retvalue << c.chr 
     75        end 
     76        retvalue 
     77      end 
     78 
     79      private 
     80      def simplified_type(field_type, type_name,limit) 
     81        ret_type = :string 
     82#         puts "typecode: [#{field_type}] [#{type_name}]" 
     83        case field_type 
     84          when 1 then ret_type = :boolean # BOOLEAN 
     85          when 2 then ret_type = :integer # INTEGER 
     86          when 4 then ret_type = :float   # FLOAT 
     87          when 10 then ret_type = :string # CHARACTER VARYING 
     88          when 11 then ret_type = :binary 
     89          when 12 then ret_type = :binary 
     90          when 13 then ret_type = :date 
     91          when 14 then ret_type = :time 
     92          when 16 then ret_type = :timestamp 
     93          when 22 then ret_type = :integer # TINYINT 
     94          else 
     95            puts "ERROR: Unknown typecode: [#{field_type}] [#{type_name}]" 
     96        end 
     97        ret_type 
     98      end 
     99    end 
     100 
     101    class FrontBaseAdapter < AbstractAdapter 
     102       
     103      def initialize(connection, logger, connection_options, config) 
     104        super(connection, logger) 
     105        @connection_options, @config = connection_options, config 
     106        @transaction_mode = :pessimistic 
     107         
     108        # threaded_connections_test.rb will fail unless we set the session 
     109        # to optimistic locking mode 
     110#         set_pessimistic_transactions 
     111#         execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC" 
     112      end 
     113 
     114      # Returns the human-readable name of the adapter.  Use mixed case - one 
     115      # can always use downcase if needed. 
     116      def adapter_name #:nodoc: 
     117        'FrontBase' 
     118      end 
     119 
     120      # Does this adapter support migrations?  Backend specific, as the 
     121      # abstract adapter always returns +false+. 
     122      def supports_migrations? #:nodoc: 
     123        true 
     124      end 
     125 
     126      def native_database_types #:nodoc 
     127        # !!! Lookup actual FB datatypes 
     128        { 
     129          :primary_key => "INTEGER DEFAULT UNIQUE PRIMARY KEY", 
     130          :string      => { :name => "VARCHAR", :limit => 255 }, 
     131          :text        => { :name => "VARCHAR", :limit => 1073741824 }, 
     132          :integer     => { :name => "INTEGER" }, 
     133          :float       => { :name => "FLOAT" }, 
     134          :datetime    => { :name => "TIMESTAMP" }, 
     135          :timestamp   => { :name => "TIMESTAMP" }, 
     136          :time        => { :name => "TIME" }, 
     137          :date        => { :name => "DATE" }, 
     138          :binary      => { :name => "BYTE VARYING" }, 
     139          :boolean     => { :name => "BOOLEAN" } 
     140        } 
     141      end 
     142 
     143 
     144      # QUOTING ================================================== 
     145 
     146      # Quotes the column value to help prevent 
     147      # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection]. 
     148      def quote(value, column = nil) 
     149        case value 
     150          when String 
     151            if column && column.type == :binary 
     152              s = column.class.string_to_binary(value).unpack("H*")[0] 
     153              "X'#{s}'" 
     154            elsif column && [:integer, :float].include?(column.type)  
     155              value.to_s 
     156            else 
     157              "'#{quote_string(value)}'" # ' (for ruby-mode) 
     158            end 
     159          when NilClass              then "NULL" 
     160          when TrueClass             then (column && column.type == :integer ? '1' : quoted_true) 
     161          when FalseClass            then (column && column.type == :integer ? '0' : quoted_false) 
     162          when Float, Fixnum, Bignum then value.to_s 
     163          when Time, Date, DateTime 
     164            if column 
     165              case column.type 
     166                when :date then "DATE '#{value.strftime("%Y-%m-%d")}'" 
     167                when :time then "TIME '#{value.strftime("%H:%M:%S")}'" 
     168                when :timestamp then "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'" 
     169              else 
     170                raise NotImplementedError, "Unknown column type!" 
     171              end # case 
     172            else # Column wasn't passed in, so try to guess the right type 
     173              if value.kind_of? Date 
     174                "DATE '#{value.strftime("%Y-%m-%d")}'" 
     175              else 
     176                if value.hour == 0 && value.min == 0 && value.sec == 0 
     177                  "TIME '#{value.strftime("%H:%M:%S")}'" 
     178                else 
     179                  "TIMESTAMP '#{quoted_date(value)}'" 
     180                end 
     181              end  
     182            end #if column 
     183          else "'#{quote_string(value.to_yaml)}'" 
     184        end #case 
     185      end # def 
     186 
     187      # Quotes a string, escaping any ' (single quote) characters. 
     188      def quote_string(s) 
     189        s.gsub(/'/, "''") # ' (for ruby-mode) 
     190      end 
     191 
     192      def quote_column_name(name) #:nodoc: 
     193        "\"#{name}\"" 
     194      end 
     195 
     196      def quoted_true 
     197        "true" 
     198      end 
     199       
     200      def quoted_false 
     201        "false" 
     202      end 
     203 
     204 
     205      # CONNECTION MANAGEMENT ==================================== 
     206 
     207      def active? 
     208          retvalue = true 
     209          begin 
     210            retvalue = true if @connection.status == 1 
     211          rescue => e 
     212            retvalue = false 
     213          end 
     214          retvalue 
     215      end 
     216 
     217      def reconnect! 
     218        @connection.close rescue nil 
     219      
     220        @connection = FBSQL_Connect.connect(@connection_options[0], 
     221        @connection_options[1], 
     222        @connection_options[2], 
     223        @connection_options[3], 
     224        @connection_options[4], 
     225        @connection_options[5], 
     226        @connection_options[6])         
     227      end 
     228 
     229      # Close this connection 
     230      def disconnect! 
     231        @connection.close rescue nil 
     232        @active = false 
     233      end 
     234 
     235      # DATABASE STATEMENTS ====================================== 
     236 
     237      # Returns an array of record hashes with the column names as keys and 
     238      # column values as values. 
     239      def select_all(sql, name = nil) #:nodoc: 
     240        fbsql = cleanup_fb_sql(sql) 
     241        retValue = [] 
     242        fbresult = query(sql, name) 
     243#         puts "select_all SQL -> #{fbsql}" 
     244        columns = fbresult.columns 
     245        fbresult.each do |row| 
     246#           puts "SQL <- #{row.inspect}" 
     247          hashed_row = {}     
     248          colnum = 0 
     249          row.each do |col| 
     250            hashed_row[columns[colnum]] = col 
     251            colnum += 1 
     252          end 
     253          retValue << hashed_row 
     254        end 
     255        retValue 
     256      end 
     257 
     258      def select_one(sql, name = nil) #:nodoc: 
     259        fbsql = cleanup_fb_sql(sql) 
     260        retValue = [] 
     261        fbresult = query(fbsql, name) 
     262#         puts "SQL -> #{fbsql}" 
     263        columns = fbresult.columns 
     264         
     265        fbresult.each do |row| 
     266#           puts "SQL <- #{row.inspect}" 
     267          hashed_row = {}     
     268          colnum = 0 
     269          row.each do |col| 
     270            hashed_row[columns[colnum]] = col 
     271            colnum += 1 
     272          end 
     273          retValue << hashed_row 
     274          break 
     275        end 
     276        fbresult.clear 
     277        retValue = retValue.first 
     278      end 
     279 
     280      def query(sql, name = nil) #:nodoc: 
     281        fbsql = cleanup_fb_sql(sql) 
     282#         puts "SQL -> #{fbsql}" 
     283        log(fbsql, name) { @connection.query(fbsql) } 
     284      rescue => e 
     285#         puts "FB Exception: #{e.inspect}" 
     286        raise e 
     287      end 
     288 
     289      def execute(sql, name = nil) #:nodoc: 
     290        fbsql = cleanup_fb_sql(sql) 
     291#         puts "SQL -> #{fbsql}" 
     292        log(fbsql, name) { @connection.query(fbsql) } 
     293      rescue ActiveRecord::StatementInvalid => e 
     294        a = e.message.scan(/Table name - \w* - exists/) 
     295        if a.length == 0 
     296          raise e 
     297        end 
     298      end 
     299       
     300      # Returns the last auto-generated ID from the affected table. 
     301      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: 
     302#         puts "SQL -> #{sql.inspect}" 
     303        result = execute(sql, name) 
     304        id_value || pk 
     305      end 
     306 
     307      # Executes the update statement and returns the number of rows affected. 
     308      def update(sql, name = nil) #:nodoc: 
     309#         puts "SQL -> #{sql.inspect}" 
     310        query(sql, name).num_rows 
     311      end 
     312 
     313      alias_method :delete, :update #:nodoc: 
     314 
     315      def set_pessimistic_transactions 
     316        if @transaction_mode == :optimistic 
     317          execute "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, LOCKING PESSIMISTIC, READ WRITE" 
     318          @transaction_mode = :pessimistic 
     319        end 
     320      end 
     321 
     322      def set_optimistic_transactions 
     323        if @transaction_mode == :pessimistic 
     324          execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC" 
     325          @transaction_mode = :optimistic 
     326        end 
     327      end 
     328 
     329      def begin_db_transaction #:nodoc: 
     330        execute "SET COMMIT FALSE" 
     331      rescue 
     332      end 
     333 
     334      def commit_db_transaction #:nodoc: 
     335        execute "COMMIT" 
     336      ensure 
     337        execute "SET COMMIT TRUE" 
     338      end 
     339 
     340      def rollback_db_transaction #:nodoc: 
     341        execute "ROLLBACK" 
     342        ensure 
     343          execute "SET COMMIT TRUE" 
     344      end 
     345 
     346      def add_limit_offset!(sql, options) #:nodoc 
     347        if limit = options[:limit] 
     348          offset = options[:offset] || 0 
     349         
     350# Here is the full syntax FrontBase supports: 
     351# (from gclem@frontbase.com) 
     352#  
     353#       TOP <limit - unsigned integer> 
     354#       TOP ( <offset expr>, <limit expr>) 
     355         
     356          # "TOP 0" is not allowed, so we have 
     357          # to use a cheap trick. 
     358          if limit == 0 
     359            if sql =~ /WHERE/i 
     360              sql.sub!(/WHERE/i, 'WHERE 0 = 1 AND ') 
     361            elsif 
     362              sql =~ /ORDER\s+BY/i 
     363              sql.sub!(/ORDER\s+BY/i, 'WHERE 0 = 1 ORDER BY') 
     364            else 
     365              sql << 'WHERE 0 = 1' 
     366            end 
     367          else 
     368            if offset == 0 
     369              sql.replace sql.gsub("SELECT ","SELECT TOP #{limit} ") 
     370            else 
     371              sql.replace sql.gsub("SELECT ","SELECT TOP(#{offset},#{limit}) ") 
     372          end 
     373        end 
     374           
     375        end 
     376      end 
     377 
     378      def prefetch_primary_key?(table_name = nil) 
     379        true 
     380      end 
     381 
     382      # Returns the next sequence value from a sequence generator. Not generally 
     383      # called directly; used by ActiveRecord to get the next primary key value 
     384      # when inserting a new database record (see #prefetch_primary_key?). 
     385      def next_sequence_value(sequence_name) 
     386        unique = select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value") 
     387         
     388        # The test cases cannot handle a zero primary key 
     389        if unique == 0 then unique = select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value") end 
     390        unique  
     391      end 
     392 
     393      def default_sequence_name(table, column) 
     394        table; 
     395      end 
     396 
     397      # Set the sequence to the max value of the table's column. 
     398      def reset_sequence!(table, column, sequence = nil) 
     399         
     400      end 
     401 
     402      def classes_for_table_name(table) 
     403        klasses = [] 
     404        # !!! Hack because subclasses is a protected method... 
     405        subclasses = ActiveRecord::Base.class_eval('@@subclasses') 
     406        subclasses.values.each do |a| 
     407          a.each {|n| klasses << n} 
     408        end 
     409        subset_klasses = klasses.select {|k| k.table_name == table} 
     410      end 
     411       
     412      def reset_pk_sequence!(table, pk = nil, sequence = nil) 
     413        klasses = classes_for_table_name(table) 
     414        klass = klasses.nil? ? nil : klasses[0] 
     415        pk = klass.primary_key if klass != nil 
     416        if pk && klass.columns_hash[pk].type == :integer 
     417          mpk = select_value("SELECT MAX(#{pk}) FROM #{table}") 
     418          execute("SET UNIQUE FOR #{klass.table_name}(#{pk})") 
     419        end 
     420         
     421      end 
     422 
     423      # SCHEMA STATEMENTS ======================================== 
     424 
     425      def structure_dump #:nodoc: 
     426        select_all("SHOW TABLES").inject("") do |structure, table| 
     427          structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n" 
     428        end 
     429      end 
     430 
     431      def recreate_database(name) #:nodoc: 
     432        drop_database(name) 
     433        create_database(name) 
     434      end 
     435 
     436      def create_database(name) #:nodoc: 
     437        execute "CREATE DATABASE #{name}" 
     438      end 
     439       
     440      def drop_database(name) #:nodoc: 
     441        puts "TRACE: drop_database" 
     442        execute "DROP DATABASE #{name}" 
     443      end 
     444 
     445 
     446      def tables(name = nil) #:nodoc: 
     447        sql = "SELECT \"TABLE_NAME\" FROM INFORMATION_SCHEMA.TABLES AS T0,INFORMATION_SCHEMA.SCHEMATA AS T1 WHERE T0.SCHEMA_PK = T1.SCHEMA_PK AND \"SCHEMA_NAME\" = CURRENT_SCHEMA" 
     448         
     449        select_values(sql,nil) 
     450      end 
     451 
     452      def indexes(table_name, name = nil)#:nodoc: 
     453        indexes = [] 
     454        current_index = nil 
     455        sql = "SELECT INDEX_NAME, T2.ORDINAL_POSITION ,INDEX_COLUMN_COUNT, INDEX_TYPE, \"COLUMN_NAME\", IS_NULLABLE FROM INFORMATION_SCHEMA.TABLES AS T0, INFORMATION_SCHEMA.INDEXES AS T1, INFORMATION_SCHEMA.INDEX_COLUMN_USAGE AS T2, INFORMATION_SCHEMA.COLUMNS AS T3 WHERE T0.\"TABLE_NAME\" = \'#{table_name}\' AND INDEX_TYPE <> 0 AND T0.TABLE_PK = T1.TABLE_PK AND T0.TABLE_PK = T2.TABLE_PK AND T0.TABLE_PK = T3.TABLE_PK AND T1. INDEXES_PK = T2.INDEX_PK AND T2.COLUMN_PK = T3.COLUMN_PK ORDER BY INDEX_NAME, T2.ORDINAL_POSITION" 
     456 
     457        columns = [] 
     458        query(sql).each do |row| 
     459          index_name   = row[0] 
     460          ord_position = row[1] 
     461          ndx_colcount = row[2] 
     462          index_type   = row[3] 
     463          column_name  = row[4] 
     464           
     465          if index_type == 1 
     466            isUnique = true 
     467          else 
     468            isUnique = false 
     469          end 
     470           
     471          columns << column_name 
     472          if ord_position == ndx_colcount 
     473            indexes << IndexDefinition.new(table_name, index_name, isUnique , columns) 
     474            columns = [] 
     475          end 
     476        end 
     477        indexes 
     478      end 
     479 
     480      def columns(table_name, name = nil)#:nodoc: 
     481        sql = "SELECT \"TABLE_NAME\", \"COLUMN_NAME\", ORDINAL_POSITION, IS_NULLABLE, COLUMN_DEFAULT, DATA_TYPE, DATA_TYPE_CODE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE, DATETIME_PRECISION, DATETIME_PRECISION_LEADING FROM INFORMATION_SCHEMA.TABLES T0, INFORMATION_SCHEMA.COLUMNS T1, INFORMATION_SCHEMA.DATA_TYPE_DESCRIPTOR T3 WHERE \"TABLE_NAME\" = '#{table_name}' AND T0.TABLE_PK = T1.TABLE_PK AND T0.TABLE_PK = T3.TABLE_OR_DOMAIN_PK AND T1.COLUMN_PK = T3.COLUMN_NAME_PK ORDER BY T1.ORDINAL_POSITION" 
     482        rawresults = query(sql,name) 
     483        columns = [] 
     484        rawresults.each do |field| 
     485          typestring = field[5] 
     486          typecode = field[6] 
     487          base = field[0] 
     488          name = field[1] 
     489          limit = field[7] 
     490          precision = field[8] 
     491          scale = field[9] 
     492          default = field[4] 
     493          nullable = field[3] 
     494          columns << FrontBaseColumn.new(base, name, typecode, typestring, limit, precision, scale, default, nullable) 
     495         end 
     496        columns 
     497      end 
     498       
     499      def create_table(name, options = {}) 
     500        table_definition = TableDefinition.new(self) 
     501        table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false 
     502 
     503        yield table_definition 
     504 
     505        if options[:force] 
     506          drop_table(name) rescue nil 
     507        end 
     508 
     509        create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " 
     510        create_sql << "#{name} (" 
     511        create_sql << table_definition.to_sql 
     512        create_sql << ") #{options[:options]}" 
     513        begin_db_transaction 
     514        execute create_sql 
     515        commit_db_transaction 
     516        rescue ActiveRecord::StatementInvalid => e 
     517          a = e.message.scan(/Table name - \w* - exists/) 
     518          raise e if a.length == 0 
     519      end 
     520       
     521      def rename_table(name, new_name) 
     522        columns = columns(name) 
     523        pkcol = columns.find {|c| c.fb_autogen} 
     524        execute "ALTER TABLE NAME #{name} TO #{new_name}" 
     525        if pkcol 
     526          change_column_default(new_name,pkcol.name,"UNIQUE") 
     527          begin_db_transaction 
     528          mpk = select_value("SELECT MAX(#{pkcol.name}) FROM #{new_name}") 
     529          mpk = 0 if mpk.nil? 
     530          execute "SET UNIQUE=#{mpk} FOR #{new_name}" 
     531          commit_db_transaction 
     532        end 
     533      end   
     534 
     535      # Drops a table from the database. 
     536      def drop_table(name) 
     537        execute "DROP TABLE #{name} RESTRICT" 
     538      rescue ActiveRecord::StatementInvalid => e 
     539        a = e.message.scan(/Referenced TABLE - \w* - does not exist/) 
     540        raise e if a.length == 0 
     541      end 
     542 
     543      # Adds a new column to the named table. 
     544      # See TableDefinition#column for details of the options you can use. 
     545      def add_column(table_name, column_name, type, options = {}) 
     546        add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}" 
     547        options[:type] = type 
     548        add_column_options!(add_column_sql, options) 
     549        execute(add_column_sql) 
     550      end 
     551 
     552      def add_column_options!(sql, options) #:nodoc: 
     553        default_value = quote(options[:default], options[:column]) 
     554        if options[:default] 
     555          if options[:type] == :boolean 
     556            default_value = options[:default] == 0 ? quoted_false : quoted_true 
     557          end 
     558        end 
     559        sql << " DEFAULT #{default_value}" unless options[:default].nil? 
     560