<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/sequel/adapters/ado/mssql.rb</filename>
    </added>
    <added>
      <filename>lib/sequel/adapters/jdbc/mssql.rb</filename>
    </added>
    <added>
      <filename>lib/sequel/adapters/odbc/mssql.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -168,7 +168,7 @@ begin
     t.spec_opts  = spec_opts.call
   end
   
-  %w'postgres sqlite mysql informix oracle ado'.each do |adapter|
+  %w'postgres sqlite mysql informix oracle firebird'.each do |adapter|
     desc &quot;Run #{adapter} specs without coverage&quot;
     Spec::Rake::SpecTask.new(&quot;spec_#{adapter}&quot;) do |t|
       t.spec_files = [&quot;spec/adapters/#{adapter}_spec.rb&quot;] + Dir[&quot;spec/integration/*_test.rb&quot;]</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,15 +1,7 @@
 require 'win32ole'
 
 module Sequel
-  # The ADO adapter provides connectivity to ADO databases in Windows. ADO
-  # databases can be opened using a URL with the ado schema:
-  #
-  #   DB = Sequel.connect('ado://mydb')
-  # 
-  # or using the Sequel.ado method:
-  #
-  #   DB = Sequel.ado('mydb')
-  #
+  # The ADO adapter provides connectivity to ADO databases in Windows.
   module ADO
     class Database &lt; Sequel::Database
       set_adapter_scheme :ado
@@ -19,20 +11,19 @@ module Sequel
         opts[:driver] ||= 'SQL Server'
         case opts[:driver]
         when 'SQL Server'
-          Sequel.require 'adapters/shared/mssql'
-          extend Sequel::MSSQL::DatabaseMethods
+          Sequel.require 'adapters/ado/mssql'
+          extend Sequel::ADO::MSSQL::DatabaseMethods
         end
       end
 
       # Connect to the database. In addition to the usual database options,
-      # the following option has effect:
+      # the following options have an effect:
       #
       # * :command_timeout - Sets the time in seconds to wait while attempting
-      #     to execute a command before cancelling the attempt and generating
-      #     an error. Specifically, it sets the ADO CommandTimeout property.
-      #     If this property is not set, the default of 30 seconds is used.
+      #   to execute a command before cancelling the attempt and generating
+      #   an error. Specifically, it sets the ADO CommandTimeout property.
+      #   If this property is not set, the default of 30 seconds is used.
       # * :provider - Sets the Provider of this ADO connection (for example, &quot;SQLOLEDB&quot;)
-
       def connect(server)
         opts = server_opts(server)
         s = &quot;driver=#{opts[:driver]};server=#{opts[:host]};database=#{opts[:database]}#{&quot;;uid=#{opts[:user]};pwd=#{opts[:password]}&quot; if opts[:user]}&quot;
@@ -50,14 +41,28 @@ module Sequel
       def execute(sql, opts={})
         log_info(sql)
         synchronize(opts[:server]) do |conn|
-          r = conn.Execute(sql)
-          yield(r) if block_given?
-          r
+          begin
+            r = conn.Execute(sql)
+            yield(r) if block_given?
+          rescue ::WIN32OLERuntimeError =&gt; e
+            raise_error(e)
+          end
         end
+        nil
+      end
+      alias do execute
+      
+      # The ADO adapter doesn't support transactions, since it appears not to
+      # use a single native connection for each connection in the pool
+      def transaction(opts={})
+        yield nil
       end
-      alias_method :do, :execute
 
       private
+      
+      def connection_pool_default_options
+        super.merge(:pool_convert_exceptions=&gt;false)
+      end
 
       def disconnect_connection(conn)
         conn.Close
@@ -67,25 +72,8 @@ module Sequel
     class Dataset &lt; Sequel::Dataset
       def fetch_rows(sql)
         execute(sql) do |s|
-          @columns = s.Fields.extend(Enumerable).map do |column|
-            name = column.Name.empty? ? '(no column name)' : column.Name
-            output_identifier(name)
-          end
-          
-          unless s.eof
-            s.moveFirst
-            s.getRows.transpose.each {|r| yield hash_row(r)}
-          end
-        end
-        self
-      end
-      
-      private
-      
-      def hash_row(row)
-        @columns.inject({}) do |m, c|
-          m[c] = row.shift
-          m
+          @columns = cols = s.Fields.extend(Enumerable).map{|column| output_identifier(column.Name)}
+          s.getRows.transpose.each{|r| yield cols.inject({}){|m,c| m[c] = r.shift; m}}
         end
       end
     end</diff>
      <filename>lib/sequel/adapters/ado.rb</filename>
    </modified>
    <modified>
      <diff>@@ -58,8 +58,8 @@ module Sequel
         Java::oracle.jdbc.driver.OracleDriver
       end,
       :sqlserver=&gt;proc do |db|
-        Sequel.require 'adapters/shared/mssql'
-        db.extend(Sequel::MSSQL::DatabaseMethods)
+        Sequel.require 'adapters/jdbc/mssql'
+        db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
         com.microsoft.sqlserver.jdbc.SQLServerDriver
       end,
       :h2=&gt;proc do |db|</diff>
      <filename>lib/sequel/adapters/jdbc.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,8 +12,8 @@ module Sequel
         super(opts)
         case opts[:db_type]
         when 'mssql'
-          Sequel.require 'adapters/shared/mssql'
-          extend Sequel::MSSQL::DatabaseMethods
+          Sequel.require 'adapters/odbc/mssql'
+          extend Sequel::ODBC::MSSQL::DatabaseMethods
         when 'progress'
           Sequel.require 'adapters/shared/progress'
           extend Sequel::Progress::DatabaseMethods
@@ -50,6 +50,8 @@ module Sequel
           begin
             r = conn.run(sql)
             yield(r) if block_given?
+          rescue ::ODBC::Error =&gt; e
+            raise_error(e)
           ensure
             r.drop if r
           end
@@ -59,12 +61,22 @@ module Sequel
       
       def execute_dui(sql, opts={})
         log_info(sql)
-        synchronize(opts[:server]){|conn| conn.do(sql)}
+        synchronize(opts[:server]) do |conn|
+          begin
+            conn.do(sql)
+          rescue ::ODBC::Error =&gt; e
+            raise_error(e)
+          end
+        end
       end
-      alias_method :do, :execute_dui
+      alias do execute_dui
 
       private
       
+      def connection_pool_default_options
+        super.merge(:pool_convert_exceptions=&gt;false)
+      end
+      
       def connection_execute_method
         :do
       end
@@ -81,19 +93,19 @@ module Sequel
       ODBC_TIMESTAMP_AFTER_SECONDS =
         ODBC_TIMESTAMP_FORMAT.index( '%S' ).succ - ODBC_TIMESTAMP_FORMAT.length
       ODBC_DATE_FORMAT = &quot;{d '%Y-%m-%d'}&quot;.freeze
-      UNTITLED_COLUMN = 'untitled_%d'.freeze
 
       def fetch_rows(sql, &amp;block)
         execute(sql) do |s|
-          untitled_count = 0
-          @columns = s.columns(true).map do |c|
-            if (n = c.name).empty?
-              n = UNTITLED_COLUMN % (untitled_count += 1)
+          i = -1
+          cols = s.columns(true).map{|c| [output_identifier(c.name), i+=1]}
+          @columns = cols.map{|c| c.at(0)}
+          if rows = s.fetch_all
+            rows.each do |row|
+              hash = {}
+              cols.each{|n,i| hash[n] = convert_odbc_value(row[i])}
+              yield hash
             end
-            output_identifier(n)
           end
-          rows = s.fetch_all
-          rows.each {|row| yield hash_row(row)} if rows
         end
         self
       end
@@ -119,15 +131,7 @@ module Sequel
           v
         end
       end
-      
-      def hash_row(row)
-        hash = {}
-        row.each_with_index do |v, idx|
-          hash[@columns[idx]] = convert_odbc_value(v)
-        end
-        hash
-      end
-      
+
       def literal_date(v)
         v.strftime(ODBC_DATE_FORMAT)
       end
@@ -149,7 +153,7 @@ module Sequel
 
       def literal_time(v)
         formatted = v.strftime(ODBC_TIMESTAMP_FORMAT)
-        formatted.insert(ODBC_TIMESTAMP_AFTER_SECONDS, &quot;.#{(v.usec.to_f/1000).round}&quot;) if usec &gt;= 1000
+        formatted.insert(ODBC_TIMESTAMP_AFTER_SECONDS, &quot;.#{(v.usec.to_f/1000).round}&quot;) if v.usec &gt;= 1000
         formatted
       end
     end</diff>
      <filename>lib/sequel/adapters/odbc.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,12 +17,42 @@ module Sequel
         ds.extend(DatasetMethods)
         ds
       end
+      
+      # Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
+      # information on tables.
+      def tables(opts={})
+        m = output_identifier_meth
+        metadata_dataset.from(:information_schema__tables___t).
+          select(:table_name).
+          filter(:table_type=&gt;'BASE TABLE', :table_schema=&gt;(opts[:schema]||default_schema||'dbo').to_s).
+          map{|x| m.call(x[:table_name])}
+      end
 
       private
 
       def auto_increment_sql
         AUTO_INCREMENT
       end
+      
+      def alter_table_sql(table, op)
+        case op[:op]
+        when :add_column
+          &quot;ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}&quot;
+        when :rename_column
+          &quot;SP_RENAME #{literal(&quot;#{quote_schema_table(table)}.#{quote_identifier(op[:name])}&quot;)}, #{literal(op[:new_name].to_s)}, 'COLUMN'&quot;
+        when :set_column_type
+          &quot;ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(op)}&quot;
+        when :set_column_null
+          sch = schema(table).find{|k,v| k.to_s == op[:name].to_s}.last
+          type = {:type=&gt;sch[:db_type]}
+          type[:size] = sch[:max_chars] if sch[:max_chars]
+          &quot;ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(type)} #{'NOT ' unless op[:null]}NULL&quot;
+        when :set_column_default
+          &quot;ALTER TABLE #{quote_schema_table(table)} ADD CONSTRAINT #{quote_identifier(&quot;sequel_#{table}_#{op[:name]}_def&quot;)} DEFAULT #{literal(op[:default])} FOR #{quote_identifier(op[:name])}&quot;
+        else
+          super(table, op)
+        end
+      end
 
       # SQL to BEGIN a transaction.
       def begin_transaction_sql
@@ -38,14 +68,57 @@ module Sequel
       def rollback_transaction_sql
         SQL_ROLLBACK
       end
+      
+      def schema_parse_table(table_name, opts)
+        m = output_identifier_meth
+        m2 = input_identifier_meth
+        ds = metadata_dataset.from(:information_schema__tables___t).
+         join(:information_schema__columns___c, :table_catalog=&gt;:table_catalog,
+              :table_schema =&gt; :table_schema, :table_name =&gt; :table_name).
+         select(:column_name___column, :data_type___db_type, :character_maximum_length___max_chars, :column_default___default, :is_nullable___allow_null).
+         filter(:c__table_name=&gt;m2.call(table_name.to_s))
+        if schema = opts[:schema] || default_schema
+          ds.filter!(:table_schema=&gt;schema)
+        end
+        ds.map do |row|
+          row[:allow_null] = row[:allow_null] == 'YES' ? true : false
+          row[:default] = nil if blank_object?(row[:default])
+          row[:type] = schema_column_type(row[:db_type])
+          [m.call(row.delete(:column)), row]
+        end
+      end
 
       # SQL fragment for marking a table as temporary
       def temporary_table_sql
         TEMPORARY
       end
+      
+      # MSSQL has both datetime and timestamp classes, most people are going
+      # to want datetime
+      def type_literal_generic_datetime(column)
+        :datetime
+      end
+
+      # MSSQL has both datetime and timestamp classes, most people are going
+      # to want datetime
+      def type_literal_generic_time(column)
+        column[:only_time] ? :time : :datetime
+      end
+      
+      # MSSQL doesn't have a true boolean class, so it uses bit
+      def type_literal_generic_trueclass(column)
+        :bit
+      end
+      
+      # MSSQL uses image type for blobs
+      def type_literal_generic_file(column)
+        :image
+      end
     end
   
     module DatasetMethods
+      BOOL_TRUE = '1'.freeze
+      BOOL_FALSE = '0'.freeze
       SELECT_CLAUSE_ORDER = %w'with limit distinct columns from table_options join where group order having compounds'.freeze
 
       def complex_expression_sql(op, args)
@@ -80,6 +153,10 @@ module Sequel
         false
       end
       
+      def supports_is_true?
+        false
+      end
+      
       # MSSQL 2005+ supports window functions
       def supports_window_functions?
         true
@@ -90,6 +167,16 @@ module Sequel
       def literal_string(v)
         &quot;N#{super}&quot;
       end
+      
+      # Use 0 for false on MySQL
+      def literal_false
+        BOOL_FALSE
+      end
+
+      # Use 1 for true on MySQL
+      def literal_true
+        BOOL_TRUE
+      end
 
       def select_clause_order
         SELECT_CLAUSE_ORDER</diff>
      <filename>lib/sequel/adapters/shared/mssql.rb</filename>
    </modified>
    <modified>
      <diff>@@ -189,7 +189,6 @@ module Sequel
       end
 
       # MySQL doesn't have a true boolean class, so it uses tinyint
-      # MySQL doesn't have a true boolean class, so it uses tinyint
       def type_literal_generic_trueclass(column)
         :tinyint
       end</diff>
      <filename>lib/sequel/adapters/shared/mysql.rb</filename>
    </modified>
    <modified>
      <diff>@@ -137,9 +137,10 @@ module Sequel
           scheme = uri.scheme
           scheme = :dbi if scheme =~ /\Adbi-/
           c = adapter_class(scheme)
-          uri_options = {}
+          uri_options = c.send(:uri_to_options, uri)
           uri.query.split('&amp;').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v} unless uri.query.to_s.strip.empty?
-          opts = c.send(:uri_to_options, uri).merge(uri_options).merge(opts)
+          uri_options.entries.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
+          opts = uri_options.merge(opts)
         end
       when Hash
         opts = conn_string.merge(opts)
@@ -860,13 +861,13 @@ module Sequel
         :datetime
       when /\Atime( with(out)? time zone)?\z/io
         :time
-      when /\Aboolean\z/io
+      when /\A(boolean|bit)\z/io
         :boolean
       when /\A(real|float|double( precision)?)\z/io
         :float
       when /\A(((numeric|decimal)(\(\d+,\d+\))?)|money)\z/io
         :decimal
-      when /bytea|blob/io
+      when /bytea|blob|image/io
         :blob
       end
     end</diff>
      <filename>lib/sequel/database.rb</filename>
    </modified>
    <modified>
      <diff>@@ -342,6 +342,7 @@ module Sequel
     # Modify the identifier returned from the database based on the
     # identifier_output_method.
     def output_identifier(v)
+      v = 'untitled' if v == ''
       (i = identifier_output_method) ? v.to_s.send(i).to_sym : v.to_sym
     end
 </diff>
      <filename>lib/sequel/dataset.rb</filename>
    </modified>
    <modified>
      <diff>@@ -419,9 +419,12 @@ module Sequel
             # returned by the schema.
             cols = schema_array.collect{|k,v| k}
             set_columns(cols)
-            # Set the primary key(s) based on the schema information
-            pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
-            pks.length &gt; 0 ? set_primary_key(*pks) : no_primary_key
+            # Set the primary key(s) based on the schema information,
+            # if the schema information includes primary key information
+            if schema_array.all?{|k,v| v.has_key?(:primary_key)}
+              pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
+              pks.length &gt; 0 ? set_primary_key(*pks) : no_primary_key
+            end
             # Also set the columns for the dataset, so the dataset
             # doesn't have to do a query to get them.
             dataset.instance_variable_set(:@columns, cols)</diff>
      <filename>lib/sequel/model/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -839,13 +839,34 @@ context &quot;A Database adapter with a scheme&quot; do
   end
 
   specify &quot;should be accessible through Sequel.connect with URL parameters&quot; do
-    c = Sequel.connect 'ccc://localhost/db?host=/tmp&amp;user=test'
+    c = Sequel.connect 'ccc:///db?host=/tmp&amp;user=test'
     c.should be_a_kind_of(CCC)
     c.opts[:host].should == '/tmp'
     c.opts[:database].should == 'db'
     c.opts[:user].should == 'test'
   end
+  
+  specify &quot;should have URL parameters take precedence over fixed URL parts&quot; do
+    c = Sequel.connect 'ccc://localhost/db?host=a&amp;database=b'
+    c.should be_a_kind_of(CCC)
+    c.opts[:host].should == 'a'
+    c.opts[:database].should == 'b'
+  end
+  
+  specify &quot;should have hash options take predence over URL parameters or parts&quot; do
+    c = Sequel.connect 'ccc://localhost/db?host=/tmp', :host=&gt;'a', :database=&gt;'b', :user=&gt;'c'
+    c.should be_a_kind_of(CCC)
+    c.opts[:host].should == 'a'
+    c.opts[:database].should == 'b'
+    c.opts[:user].should == 'c'
+  end
 
+  specify &quot;should unescape values of URL parameters and parts&quot; do
+    c = Sequel.connect 'ccc:///d%5bb%5d?host=domain%5cinstance'
+    c.should be_a_kind_of(CCC)
+    c.opts[:database].should == 'd[b]'
+    c.opts[:host].should == 'domain\\instance'
+  end
 end
 
 context &quot;Sequel::Database.connect&quot; do</diff>
      <filename>spec/core/database_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -92,6 +92,16 @@ context &quot;Dataset&quot; do
     @dataset.identifier_output_method = :reverse
     @dataset.send(:output_identifier, &quot;at_b_C&quot;).should == :C_b_ta
   end
+  
+  specify &quot;should have output_identifier handle empty identifiers&quot; do
+    @dataset.send(:output_identifier, &quot;&quot;).should == :untitled
+    @dataset.identifier_output_method = :upcase
+    @dataset.send(:output_identifier, &quot;&quot;).should == :UNTITLED
+    @dataset.identifier_output_method = :downcase
+    @dataset.send(:output_identifier, &quot;&quot;).should == :untitled
+    @dataset.identifier_output_method = :reverse
+    @dataset.send(:output_identifier, &quot;&quot;).should == :deltitnu
+  end
 end
 
 context &quot;Dataset#clone&quot; do</diff>
      <filename>spec/core/dataset_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -812,6 +812,7 @@ context &quot;Schema Parser&quot; do
     @db.schema(:&quot;time with time zone&quot;).first.last[:type].should == :time
     @db.schema(:&quot;time without time zone&quot;).first.last[:type].should == :time
     @db.schema(:boolean).first.last[:type].should == :boolean
+    @db.schema(:bit).first.last[:type].should == :boolean
     @db.schema(:real).first.last[:type].should == :float
     @db.schema(:float).first.last[:type].should == :float
     @db.schema(:double).first.last[:type].should == :float
@@ -820,5 +821,7 @@ context &quot;Schema Parser&quot; do
     @db.schema(:decimal).first.last[:type].should == :decimal
     @db.schema(:money).first.last[:type].should == :decimal
     @db.schema(:bytea).first.last[:type].should == :blob
+    @db.schema(:blob).first.last[:type].should == :blob
+    @db.schema(:image).first.last[:type].should == :blob
   end
 end</diff>
      <filename>spec/core/schema_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -454,25 +454,65 @@ context &quot;Model.db_schema&quot; do
     @c.db_schema.should == {:x=&gt;{:type=&gt;:integer}, :z=&gt;{}}
   end
 
-  specify &quot;should not use schema_for_table if the dataset uses multiple tables or custom sql&quot; do
+  specify &quot;should not use schema if the dataset uses multiple tables or custom sql&quot; do
     ds = @dataset.join(:x, :id)
     d = ds.db
     e = false
-    d.meta_def(:schema){|table| e = true}
+    d.meta_def(:schema){|table, *opts| e = true}
     def @c.columns; [:x]; end
     @c.dataset = ds
     @c.db_schema.should == {:x=&gt;{}}
     e.should == false
   end
 
-  specify &quot;should fallback to fetching records if schema_for_table raises an error&quot; do
+  specify &quot;should fallback to fetching records if schema raises an error&quot; do
     ds = @dataset.join(:x, :id)
     d = ds.db
-    def d.schema(table)
+    def d.schema(table, opts={})
       raise StandardError
     end
     def @c.columns; [:x]; end
     @c.dataset = ds
     @c.db_schema.should == {:x=&gt;{}}
   end
+  
+  specify &quot;should automatically set a singular primary key based on the schema&quot; do
+    ds = @dataset
+    d = ds.db
+    d.meta_def(:schema){|table, *opts| [[:x, {:primary_key=&gt;true}]]}
+    @c.primary_key.should == :id
+    @c.dataset = ds
+    @c.db_schema.should == {:x=&gt;{:primary_key=&gt;true}}
+    @c.primary_key.should == :x
+  end
+  
+  specify &quot;should automatically set the composite primary key based on the schema&quot; do
+    ds = @dataset
+    d = ds.db
+    d.meta_def(:schema){|table, *opts| [[:x, {:primary_key=&gt;true}], [:y, {:primary_key=&gt;true}]]}
+    @c.primary_key.should == :id
+    @c.dataset = ds
+    @c.db_schema.should == {:x=&gt;{:primary_key=&gt;true}, :y=&gt;{:primary_key=&gt;true}}
+    @c.primary_key.should == [:x, :y]
+  end
+  
+  specify &quot;should automatically set no primary key based on the schema&quot; do
+    ds = @dataset
+    d = ds.db
+    d.meta_def(:schema){|table, *opts| [[:x, {:primary_key=&gt;false}], [:y, {:primary_key=&gt;false}]]}
+    @c.primary_key.should == :id
+    @c.dataset = ds
+    @c.db_schema.should == {:x=&gt;{:primary_key=&gt;false}, :y=&gt;{:primary_key=&gt;false}}
+    @c.primary_key.should == nil
+  end
+  
+  specify &quot;should not modify the primary key unless all column schema hashes have a :primary_key entry&quot; do
+    ds = @dataset
+    d = ds.db
+    d.meta_def(:schema){|table, *opts| [[:x, {:primary_key=&gt;false}], [:y, {}]]}
+    @c.primary_key.should == :id
+    @c.dataset = ds
+    @c.db_schema.should == {:x=&gt;{:primary_key=&gt;false}, :y=&gt;{}}
+    @c.primary_key.should == :id
+  end
 end</diff>
      <filename>spec/model/model_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>spec/adapters/ado_spec.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>e4d905f547020fe89a9ff91912a0ab6a718a9e26</id>
    </parent>
  </parents>
  <author>
    <name>Jeremy Evans</name>
    <email>code@jeremyevans.net</email>
  </author>
  <url>http://github.com/jeremyevans/sequel/commit/f44d7916a62d9eacf3b58f9f366c1dccbc0c1f94</url>
  <id>f44d7916a62d9eacf3b58f9f366c1dccbc0c1f94</id>
  <committed-date>2009-07-07T12:56:56-07:00</committed-date>
  <authored-date>2009-07-07T12:35:00-07:00</authored-date>
  <message>Much better support for Microsoft SQL Server

This substantial commit provides much better support for Microsoft
SQL Server.  Specific MSSQL subadapters were added for the ado, odbc,
and jdbc adapters.  Mostly the subadapters involve getting insert
working properly to return the last inserted id.

While making these changes I've noticed that the ado adapter has huge
problems, enough so that I wouldn't recommend that anyone use it.  It
doesn't use a stable native connection, which means it can't work
correctly with transactions.  It requires a pretty ugly hack to get
insert to return the id inserted.

Transactions on ado now unconditionally yield nil.  I thought about
them raising an exception instead, but that would make the ado
adapter not work well with models (without fiddling).  It's possible
the behavior will be changed in the future.

As bad as the ado adapter is now, it's still much better than before.
Before, the ado adapter would run all queries twice when fetching
rows, and if you did any nonidempotent actions inside the SQL, you'd
have problems (as I found out when I used the ugly hack to get
insert to return the id inserted).

The ado and odbc adapters now catch the native exceptions and raise
Sequel::DatabaseError exceptions.  Also, the behavior to handle
blank identifiers has been standardized.  Sequel will now assume an
identifier of 'untitled' if a blank identifier is given.

The shared MSSQL adapter now supports Database#tables and
Database#schema, using the INFORMATION_SCHEMA views (very similarly
to what was used in Sequel 2.0).  Now, it also supports add_column,
rename_column, set_columns_type, set_column_null, and
set_column_default.

The shared MSSQL's schema method doesn't include primary key info, so
some of the model logic changed so that it doesn't try to set
no primary key unless all schema hashes include a primary key entry.

The shared MSSQL adapter now uses the datetime type instead of the
timestamp type for generic datetimes, and uses bit and image for
boolean and file types.  It uses 0 and 1 for false and true, and
no longer attempts to use IS TRUE.

The odbc adapter's literal_time method has been fixed.

In order to ease the connection to MSSQL servers with instances
using a connection string, Sequel now will unescape URL parts. So the
following now works:

  Sequel.connect(ado:///db?host=server%5cinstance)

The ado adapter specs were removed, because the ado adapter itself
doesn't really have any specific behavior that should be tested.  Now
that Sequel has the generic integration tests, those should be used
instead.  I removed the spec_ado rake task. and replaced it with a
spec_firebird rake task.

Here's the results for integration testing on MSSQL with each
adapter:

* ado: 115 examples, 42 failures
* jdbc: 117 examples, 22 failures
* odbc: 115 examples, 19 failures

Many of the remaining failures are due to the fact that some tests
try to insert values into an autoincrementing primary key field,
which MSSQL doesn't allow.  Those tests should be refactored unless
they are explicitly testing that feature.</message>
  <tree>cb3921a89d5d32c99454c4bc9f388f1b77c5f950</tree>
  <committer>
    <name>Jeremy Evans</name>
    <email>code@jeremyevans.net</email>
  </committer>
</commit>
