Class: PGI::Dataset::Query
- Inherits:
-
Object
- Object
- PGI::Dataset::Query
- Defined in:
- lib/pgi/dataset/query.rb
Instance Attribute Summary collapse
-
#params ⇒ Array
readonly
Get the params for placeholder substitution.
Instance Method Summary collapse
-
#count ⇒ Integer
Get the number of records in a result set.
-
#each ⇒ Object
Loop through records in a result set.
-
#explain ⇒ String
Explain some query.
-
#first ⇒ Hash
Get the first record in a result set.
-
#initialize(database, table, command, **options) ⇒ Query
constructor
Create instance of Query.
-
#keyset(sort_by, cursor_id, sort_dir) ⇒ Query
Apply keyset pagination: orders by (sort_by, id) and, when a cursor id is given, seeks past that row.
-
#limit(number) ⇒ Query
Adds a LIMIT clause to the query.
-
#order(column, direction = :asc) ⇒ Query
Adds a ORDER BY clause to the query - suports multiple calls to the method.
-
#sql ⇒ String
Get the Query SQL string prepared for execution.
-
#to_a ⇒ Array
Get all the records in a result set.
-
#to_s ⇒ String
(also: #inspect)
Get a string representation of the instance.
-
#where(clause = nil, params = []) ⇒ Query
Adds a WHERE clause to the query - can only be called once per query, so combine all conditions in a single call.
Constructor Details
#initialize(database, table, command, **options) ⇒ Query
Create instance of Query
14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/pgi/dataset/query.rb', line 14 def initialize(database, table, command, **) @database = database @table = table @command = command || "SELECT * FROM #{@table}" @scope = .fetch(:scope, nil) @where = .fetch(:where, nil) @params = .fetch(:params, []) @order = .fetch(:order, {}) @limit = .fetch(:limit, 10) @returning = .fetch(:returning, nil) end |
Instance Attribute Details
#params ⇒ Array (readonly)
Get the params for placeholder substitution
137 138 139 |
# File 'lib/pgi/dataset/query.rb', line 137 def params @params end |
Instance Method Details
#count ⇒ Integer
Get the number of records in a result set
189 190 191 192 193 |
# File 'lib/pgi/dataset/query.rb', line 189 def count @command = "SELECT COUNT(*) FROM #{@table}" @order = {} first&.fetch("count", 0) end |
#each ⇒ Object
Loop through records in a result set
159 160 161 162 163 |
# File 'lib/pgi/dataset/query.rb', line 159 def each(&) @database .exec_stmt(Utils.stmt_name(@table, sql), sql, params) .each(&) end |
#explain ⇒ String
Explain some query
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/pgi/dataset/query.rb', line 168 def explain explain_sql = "EXPLAIN " << sql.dup.tap do |s| params.each_with_index do |x, i| x = case x when String "'#{x}'" else x end s.gsub!("$#{i + 1}", x.to_s) end end @database.exec(explain_sql)&.values&.join("\n") end |
#first ⇒ Hash
Get the first record in a result set
142 143 144 145 146 147 |
# File 'lib/pgi/dataset/query.rb', line 142 def first limit(1) @database .exec_stmt(Utils.stmt_name(@table, sql), sql, params) .first end |
#keyset(sort_by, cursor_id, sort_dir) ⇒ Query
Apply keyset pagination: orders by (sort_by, id) and, when a cursor id is given, seeks past that row. A nil cursor_id is the first page. The cursor predicate combines with any existing WHERE clause, so call it after #where. For sort_by == :id: WHERE id > $cursor_id For sort_by != :id: WHERE (sort_col, id) > (SELECT sort_col, id FROM table WHERE id = $cursor_id) Do not combine with a conflicting #order call — pages are only correct when the leading sort columns match the cursor predicate.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/pgi/dataset/query.rb', line 97 def keyset(sort_by, cursor_id, sort_dir) order(sort_by, sort_dir) order(:id, sort_dir) unless sort_by.to_sym == :id return self unless cursor_id op = sort_dir == :asc ? ">" : "<" id_col = Utils.sanitize_columns(:id, @table).first @params << cursor_id clause = if sort_by.to_sym == :id "#{id_col} #{op} $#{@params.size}" else sort_col = Utils.sanitize_columns(sort_by, @table).first "(#{sort_col}, #{id_col}) #{op} (SELECT #{sort_col}, #{id_col} FROM #{@table} WHERE #{id_col} = $#{@params.size})" end @where = @where ? "#{clause} AND (#{@where})" : clause self end |
#limit(number) ⇒ Query
Adds a LIMIT clause to the query
78 79 80 81 82 83 |
# File 'lib/pgi/dataset/query.rb', line 78 def limit(number) raise "LIMIT must be an integer or nil" unless number.nil? || number.is_a?(Integer) @limit = number self end |
#order(column, direction = :asc) ⇒ Query
Adds a ORDER BY clause to the query - suports multiple calls to the method
66 67 68 69 70 71 |
# File 'lib/pgi/dataset/query.rb', line 66 def order(column, direction = :asc) raise "Invalid ORDER BY direction: #{direction.inspect}" unless %i[asc desc].include?(direction) @order[Utils.sanitize_columns(column, @table)] = direction.to_s.upcase self end |
#sql ⇒ String
Get the Query SQL string prepared for execution
121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/pgi/dataset/query.rb', line 121 def sql # Simple Scope implementation scope = @scope.dup scope << " AND " if scope && @where command = @command.dup command << " WHERE #{scope}#{@where}" if @where || scope command << " ORDER BY #{Array(@order).map { |x| x.join(" ") }.join(", ")}" unless @order.empty? command << " LIMIT #{@limit}" if @limit command << " RETURNING *" if @command =~ /^UPDATE|INSERT|DELETE/ command end |
#to_a ⇒ Array
Get all the records in a result set
152 153 154 155 156 |
# File 'lib/pgi/dataset/query.rb', line 152 def to_a @database .exec_stmt(Utils.stmt_name(@table, sql), sql, params) .to_a end |
#to_s ⇒ String Also known as: inspect
Get a string representation of the instance
198 199 200 |
# File 'lib/pgi/dataset/query.rb', line 198 def to_s "#<PGI::Dataset::Query:#{object_id} @sql=#{sql} @params=#{params}>" end |
#where(clause = nil, params = []) ⇒ Query
Adds a WHERE clause to the query - can only be called once per query, so combine all conditions in a single call
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/pgi/dataset/query.rb', line 33 def where(clause = nil, params = []) return self unless clause return self if clause.empty? raise "WHERE clause already set - combine conditions in a single call" if @where case clause when Hash clause = clause.map do |k, v| @params << v "#{Utils.sanitize_columns(k, @table).first} = $#{@params.size}" end.join(" AND ") when String raise "Use placeholders in WHERE clause" if clause =~ /=(?!\s*[?$])/ offset = @params.size @params += params clause = clause.gsub(/([=<>]{1}\s{0,})(\?)/).with_index { |_, i| "#{Regexp.last_match(1)}$#{offset + i + 1}" } else raise "WHERE clause can either be a Hash or a String" end @where = clause self end |