|
2 | 2 |
|
3 | 3 | module Arel # :nodoc: all
|
4 | 4 | module Nodes
|
5 |
| - ### |
6 |
| - # Abstract base class for all AST nodes |
| 5 | + # = Using +Arel::Nodes::Node+ |
| 6 | + # |
| 7 | + # Active Record uses Arel to compose SQL statements. Instead of building SQL strings directly, it's building an |
| 8 | + # abstract syntax tree (AST) of the statement using various types of Arel::Nodes::Node. Each node represents a |
| 9 | + # fragment of a SQL statement. |
| 10 | + # |
| 11 | + # The intermediate representation allows Arel to compile the statement into the database's specific SQL dialect |
| 12 | + # only before sending it without having to care about the nuances of each database when building the statement. |
| 13 | + # It also allows easier composition of statements without having to resort to (brittle and unsafe) string manipulation. |
| 14 | + # |
| 15 | + # == Building constraints |
| 16 | + # |
| 17 | + # One of the most common use cases of Arel is generating constraints for +SELECT+ statements. To help with that, |
| 18 | + # most nodes include a couple of useful factory methods to create subtree structures for common constraints. For |
| 19 | + # a full list of those, please refer to Arel::Predications. |
| 20 | + # |
| 21 | + # The following example creates a equality constraint where the value of the name column on the users table |
| 22 | + # matches the value DHH. |
| 23 | + # |
| 24 | + # users = Arel::Table.new(:users) |
| 25 | + # constraint = users[:name].eq("DHH") |
| 26 | + # |
| 27 | + # # => Arel::Nodes::Equality.new( |
| 28 | + # # Arel::Attributes::Attribute.new(users, "name"), |
| 29 | + # # Arel::Nodes::Casted.new( |
| 30 | + # # "DHH", |
| 31 | + # # Arel::Attributes::Attribute.new(users, "name") |
| 32 | + # # ) |
| 33 | + # # ) |
| 34 | + # |
| 35 | + # The resulting SQL fragment will look like this: |
| 36 | + # |
| 37 | + # "users"."name" = 'DHH' |
| 38 | + # |
| 39 | + # The constraint fragments can be used with regular ActiveRecord::Relation objects instead of as Hash. The |
| 40 | + # following two example shows two ways of creating the same query. |
| 41 | + # |
| 42 | + # User.where(name: 'DHH') |
| 43 | + # |
| 44 | + # # SELECT "users".* FROM "users" WHERE "users"."name" = 'DHH' |
| 45 | + # |
| 46 | + # users = User.arel_table |
| 47 | + # |
| 48 | + # User.where(users[:name].eq('DHH')) |
| 49 | + # |
| 50 | + # # SELECT "users".* FROM "users" WHERE "users"."name" = 'DHH' |
| 51 | + # |
| 52 | + # == Functions |
| 53 | + # |
| 54 | + # Arel comes with built in support for SQL functions like +COUNT+, +SUM+, +MIN+, +MAX+, and +AVG+. The |
| 55 | + # Arel::Expressions module includes factory methods for the default functions. |
| 56 | + # |
| 57 | + # employees = Employee.arel_table |
| 58 | + # |
| 59 | + # Employee.select(employees[:department_id], employees[:salary].average).group(employees[:department_id]) |
| 60 | + # |
| 61 | + # # SELECT "employees"."department_id", AVG("employees"."salary") |
| 62 | + # # FROM "employees" GROUP BY "employees"."department_id" |
| 63 | + # |
| 64 | + # It’s also possible to use custom functions by using the Arel::Nodes::NamedFunction node type. It accepts a |
| 65 | + # function name and an array of parameters. |
| 66 | + # |
| 67 | + # Arel::Nodes::NamedFunction.new('date_trunc', [Arel::Nodes.build_quoted('day'), User.arel_table[:created_at]]) |
| 68 | + # |
| 69 | + # # date_trunc('day', "users"."created_at") |
| 70 | + # |
| 71 | + # == Quoting & bind params |
| 72 | + # |
| 73 | + # Values that you pass to Arel nodes need to be quoted or wrapped in bind params. This ensures they are properly |
| 74 | + # converted into the correct format without introducing a possible SQL injection vulnerability. Most factory |
| 75 | + # methods (like +eq+, +gt+, +lteq+, …) quote passed values automatically. When not using a factory method, it’s |
| 76 | + # possible to convert a value and wrap it in a Arel::Nodes::Quoted node (if necessary) by calling +Arel::Nodes. |
| 77 | + # build_quoted+. |
| 78 | + # |
| 79 | + # Arel::Nodes.build_quoted("foo") # 'foo' |
| 80 | + # Arel::Nodes.build_quoted(12.3) # 12.3 |
| 81 | + # |
| 82 | + # Instead of quoting values and embedding them directly in the SQL statement, it’s also possible to create bind |
| 83 | + # params. This keeps the actual values outside of the statement and allows using the prepared statement feature |
| 84 | + # of some databases. |
| 85 | + # |
| 86 | + # attribute = ActiveRecord::Relation::QueryAttribute.new(:name, "DHH", ActiveRecord::Type::String.new) |
| 87 | + # Arel::Nodes::BindParam.new(attribute) |
| 88 | + # |
| 89 | + # When ActiveRecord runs the query, bind params are replaced by placeholders (like +$1+) and the values are passed |
| 90 | + # separately. |
| 91 | + # |
| 92 | + # == SQL Literals |
| 93 | + # |
| 94 | + # For cases where there is no way to represent a particular SQL fragment using Arel nodes, you can use an SQL |
| 95 | + # literal. SQL literals are strings that Arel will treat “as is”. |
| 96 | + # |
| 97 | + # Arel.sql('LOWER("users"."name")').eq('dhh') |
| 98 | + # |
| 99 | + # # LOWER("users"."name") = 'dhh' |
| 100 | + # |
| 101 | + # Please keep in mind that passing data as raw SQL literals might introduce a possible SQL injection. |
7 | 102 | class Node
|
8 | 103 | include Arel::FactoryMethods
|
9 | 104 |
|
|
0 commit comments