From 6e9cf0cb4390f1d210edbecc660290aaea6b4d72 Mon Sep 17 00:00:00 2001
From: Emilio Tagua <miloops@gmail.com>
Date: Mon, 15 Feb 2010 17:20:24 -0300
Subject: Extract SQL logic from Arel::Relation into compilers.

---
 lib/arel/engines/sql/compilers/mysql_compiler.rb   |  7 ++
 .../engines/sql/compilers/postgresql_compiler.rb   | 33 +++++++++
 lib/arel/engines/sql/compilers/sqlite_compiler.rb  |  9 +++
 lib/arel/engines/sql/engine.rb                     |  3 +-
 lib/arel/engines/sql/relations.rb                  |  1 +
 lib/arel/engines/sql/relations/compiler.rb         | 63 +++++++++++++++++
 lib/arel/engines/sql/relations/relation.rb         | 78 ++--------------------
 lib/arel/engines/sql/relations/table.rb            |  8 +++
 .../sql/relations/utilities/externalization.rb     |  2 +-
 9 files changed, 130 insertions(+), 74 deletions(-)
 create mode 100644 lib/arel/engines/sql/compilers/mysql_compiler.rb
 create mode 100644 lib/arel/engines/sql/compilers/postgresql_compiler.rb
 create mode 100644 lib/arel/engines/sql/compilers/sqlite_compiler.rb
 create mode 100644 lib/arel/engines/sql/relations/compiler.rb

(limited to 'lib/arel/engines')

diff --git a/lib/arel/engines/sql/compilers/mysql_compiler.rb b/lib/arel/engines/sql/compilers/mysql_compiler.rb
new file mode 100644
index 0000000000..e3cf1f2add
--- /dev/null
+++ b/lib/arel/engines/sql/compilers/mysql_compiler.rb
@@ -0,0 +1,7 @@
+module Arel
+  module SqlCompiler
+    class MySQLCompiler < GenericCompiler
+    end
+  end
+end
+
diff --git a/lib/arel/engines/sql/compilers/postgresql_compiler.rb b/lib/arel/engines/sql/compilers/postgresql_compiler.rb
new file mode 100644
index 0000000000..c3360b53a5
--- /dev/null
+++ b/lib/arel/engines/sql/compilers/postgresql_compiler.rb
@@ -0,0 +1,33 @@
+module Arel
+  module SqlCompiler
+    class PostgreSQLCompiler < GenericCompiler
+
+      def select_sql
+        if !orders.blank? && using_distinct_on?
+          # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
+          # by wrapping the +sql+ string as a sub-select and ordering in that query.
+          order = order_clauses.join(', ').split(',').map { |s| s.strip }.reject(&:blank?)
+          order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{'DESC' if s =~ /\bdesc$/i}" }.join(', ')
+
+          query = build_query \
+            "SELECT #{select_clauses.kind_of?(::Array) ? select_clauses.join("") : select_clauses.to_s}",
+            "FROM #{from_clauses}",
+            (joins(self) unless joins(self).blank? ),
+            ("WHERE #{where_clauses.join(" AND ")}" unless wheres.blank? ),
+            ("GROUP BY #{group_clauses.join(', ')}" unless groupings.blank? ),
+            ("HAVING #{having_clauses.join(', ')}" unless havings.blank? ),
+            ("#{locked}" unless locked.blank? )
+
+          build_query \
+            "SELECT * FROM (#{query}) AS id_list",
+            "ORDER BY #{order}",
+            ("LIMIT #{taken}" unless taken.blank? ),
+            ("OFFSET #{skipped}" unless skipped.blank? )
+
+        else
+          super
+        end
+      end
+    end
+  end
+end
diff --git a/lib/arel/engines/sql/compilers/sqlite_compiler.rb b/lib/arel/engines/sql/compilers/sqlite_compiler.rb
new file mode 100644
index 0000000000..c2f78cd235
--- /dev/null
+++ b/lib/arel/engines/sql/compilers/sqlite_compiler.rb
@@ -0,0 +1,9 @@
+module Arel
+  module SqlCompiler
+    class SQLiteCompiler < GenericCompiler
+      def locked
+        nil
+      end
+    end
+  end
+end
diff --git a/lib/arel/engines/sql/engine.rb b/lib/arel/engines/sql/engine.rb
index eb9dd85602..5bb8463699 100644
--- a/lib/arel/engines/sql/engine.rb
+++ b/lib/arel/engines/sql/engine.rb
@@ -1,12 +1,13 @@
 module Arel
   module Sql
     class Engine
+
       def initialize(ar = nil)
         @ar = ar
       end
 
       def connection
-        @ar.connection
+        @ar ? @ar.connection : nil
       end
 
       def adapter_name
diff --git a/lib/arel/engines/sql/relations.rb b/lib/arel/engines/sql/relations.rb
index 8360a1f806..afe6ac775f 100644
--- a/lib/arel/engines/sql/relations.rb
+++ b/lib/arel/engines/sql/relations.rb
@@ -2,6 +2,7 @@ require 'arel/engines/sql/relations/utilities/compound'
 require 'arel/engines/sql/relations/utilities/recursion'
 require 'arel/engines/sql/relations/utilities/externalization'
 require 'arel/engines/sql/relations/utilities/nil'
+require 'arel/engines/sql/relations/compiler'
 require 'arel/engines/sql/relations/relation'
 require 'arel/engines/sql/relations/table'
 require 'arel/engines/sql/relations/operations/join'
diff --git a/lib/arel/engines/sql/relations/compiler.rb b/lib/arel/engines/sql/relations/compiler.rb
new file mode 100644
index 0000000000..6026fc126f
--- /dev/null
+++ b/lib/arel/engines/sql/relations/compiler.rb
@@ -0,0 +1,63 @@
+module Arel
+  module SqlCompiler
+    class GenericCompiler
+      attr_reader :relation
+
+      def initialize(relation)
+        @relation = relation
+      end
+
+      def select_sql
+        build_query \
+          "SELECT     #{select_clauses.join(', ')}",
+          "FROM       #{from_clauses}",
+          (joins(self)                                   unless joins(self).blank? ),
+          ("WHERE     #{where_clauses.join(" AND ")}"    unless wheres.blank?      ),
+          ("GROUP BY  #{group_clauses.join(', ')}"       unless groupings.blank?   ),
+          ("HAVING    #{having_clauses.join(', ')}"      unless havings.blank?     ),
+          ("ORDER BY  #{order_clauses.join(', ')}"       unless orders.blank?      ),
+          ("LIMIT     #{taken}"                          unless taken.blank?       ),
+          ("OFFSET    #{skipped}"                        unless skipped.blank?     ),
+          ("#{locked}"                                   unless locked.blank?)
+      end
+
+    protected
+      def method_missing(method, *args, &block)
+        relation.send(method, *args, &block)
+      end
+
+      def build_query(*parts)
+        parts.compact.join(" ")
+      end
+
+      def from_clauses
+        sources.blank? ? table_sql(Sql::TableReference.new(relation)) : sources
+      end
+
+      def select_clauses
+        attributes.collect { |a| a.to_sql(Sql::SelectClause.new(relation)) }
+      end
+
+      def where_clauses
+        wheres.collect { |w| w.to_sql(Sql::WhereClause.new(relation)) }
+      end
+
+      def group_clauses
+        groupings.collect { |g| g.to_sql(Sql::GroupClause.new(relation)) }
+      end
+
+      def having_clauses
+        havings.collect { |g| g.to_sql(Sql::HavingClause.new(relation)) }
+      end
+
+      def order_clauses
+        orders.collect { |o| o.to_sql(Sql::OrderClause.new(relation)) }
+      end
+
+      def using_distinct_on?
+        select_clauses.any? { |x| x =~ /DISTINCT ON/ }
+      end
+    end
+
+  end
+end
diff --git a/lib/arel/engines/sql/relations/relation.rb b/lib/arel/engines/sql/relations/relation.rb
index 78508595fd..15903412d5 100644
--- a/lib/arel/engines/sql/relations/relation.rb
+++ b/lib/arel/engines/sql/relations/relation.rb
@@ -1,86 +1,20 @@
 module Arel
   class Relation
-    def to_sql(formatter = Sql::SelectStatement.new(self))
-      formatter.select select_sql, self
-    end
-
-    def select_sql
-      if engine.adapter_name == "PostgreSQL" && !orders.blank? && using_distinct_on?
-        # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
-        # by wrapping the +sql+ string as a sub-select and ordering in that query.
-        order = order_clauses.join(', ').split(',').map { |s| s.strip }.reject(&:blank?)
-        order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{'DESC' if s =~ /\bdesc$/i}" }.join(', ')
-
-        query = build_query \
-          "SELECT     #{select_clauses.kind_of?(::Array) ? select_clauses.join("") : select_clauses.to_s}",
-          "FROM       #{from_clauses}",
-          (joins(self)                                   unless joins(self).blank? ),
-          ("WHERE     #{where_clauses.join(" AND ")}"    unless wheres.blank?      ),
-          ("GROUP BY  #{group_clauses.join(', ')}"       unless groupings.blank?   ),
-          ("HAVING    #{having_clauses.join(', ')}"      unless havings.blank?   ),
-          ("#{locked}"                                   unless locked.blank?     )
 
-        build_query \
-          "SELECT * FROM (#{query}) AS id_list",
-          "ORDER BY #{order}",
-          ("LIMIT     #{taken}"                          unless taken.blank?       ),
-          ("OFFSET    #{skipped}"                        unless skipped.blank?     )
-
-      else
-        build_query \
-          "SELECT     #{select_clauses.join(', ')}",
-          "FROM       #{from_clauses}",
-          (joins(self)                                   unless joins(self).blank? ),
-          ("WHERE     #{where_clauses.join(" AND ")}"    unless wheres.blank?      ),
-          ("GROUP BY  #{group_clauses.join(', ')}"       unless groupings.blank?   ),
-          ("HAVING    #{having_clauses.join(', ')}"      unless havings.blank?     ),
-          ("ORDER BY  #{order_clauses.join(', ')}"       unless orders.blank?      ),
-          ("LIMIT     #{taken}"                          unless taken.blank?       ),
-          ("OFFSET    #{skipped}"                        unless skipped.blank?     ),
-          ("#{locked}"                                   unless engine.adapter_name =~ /SQLite/ || locked.blank?)
-      end
+    def compiler
+      @compiler ||= "Arel::SqlCompiler::#{engine.adapter_name}Compiler".constantize.new(self)
     end
 
-    def inclusion_predicate_sql
-      "IN"
+    def to_sql(formatter = Sql::SelectStatement.new(self))
+      formatter.select compiler.select_sql, self
     end
 
     def christener
       @christener ||= Sql::Christener.new
     end
 
-  protected
-
-    def build_query(*parts)
-      parts.compact.join(" ")
-    end
-
-    def from_clauses
-      sources.blank? ? table_sql(Sql::TableReference.new(self)) : sources
-    end
-
-    def select_clauses
-      attributes.collect { |a| a.to_sql(Sql::SelectClause.new(self)) }
-    end
-
-    def where_clauses
-      wheres.collect { |w| w.to_sql(Sql::WhereClause.new(self)) }
-    end
-
-    def group_clauses
-      groupings.collect { |g| g.to_sql(Sql::GroupClause.new(self)) }
-    end
-
-    def having_clauses
-      havings.collect { |g| g.to_sql(Sql::HavingClause.new(self)) }
-    end
-
-    def order_clauses
-      orders.collect { |o| o.to_sql(Sql::OrderClause.new(self)) }
-    end
-
-    def using_distinct_on?
-      select_clauses.any? { |x| x =~ /DISTINCT ON/ }
+    def inclusion_predicate_sql
+      "IN"
     end
   end
 end
diff --git a/lib/arel/engines/sql/relations/table.rb b/lib/arel/engines/sql/relations/table.rb
index a409d8223f..9ea07a00a1 100644
--- a/lib/arel/engines/sql/relations/table.rb
+++ b/lib/arel/engines/sql/relations/table.rb
@@ -15,6 +15,14 @@ module Arel
       else
         @engine = options # Table.new('foo', engine)
       end
+
+      if @engine.connection
+        begin
+          require "lib/arel/engines/sql/compilers/#{@engine.adapter_name.downcase}_compiler"
+        rescue LoadError
+          raise "#{@engine.adapter_name} is not supported by Arel."
+        end
+      end
     end
 
     def as(table_alias)
diff --git a/lib/arel/engines/sql/relations/utilities/externalization.rb b/lib/arel/engines/sql/relations/utilities/externalization.rb
index 7f937e8423..a0230e90f3 100644
--- a/lib/arel/engines/sql/relations/utilities/externalization.rb
+++ b/lib/arel/engines/sql/relations/utilities/externalization.rb
@@ -3,7 +3,7 @@ module Arel
     include Recursion::BaseCase
 
     def table_sql(formatter = Sql::TableReference.new(relation))
-      formatter.select relation.select_sql, self
+      formatter.select relation.compiler.select_sql, self
     end
 
     # REMOVEME
-- 
cgit v1.2.3