Skip to content

Commit

Permalink
Adding some documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Yacine Petitprez committed Jul 8, 2018
1 parent 3979d7b commit 474d7d1
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 15 deletions.
11 changes: 10 additions & 1 deletion src/clear/sql/query/before_query.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ module Clear::SQL::Query::BeforeQuery
macro included
@before_query_triggers : Array(-> Void)

# A hook to apply some operation just before the query is executed.
#
# ```crystal
# call = 0
# req = Clear::SQL.select("1").before_query{ call += 1 }
# 10.times{ req.execute }
# pp call # 10
# ```
def before_query(&block : -> Void)
@before_query_triggers << block
end

def trigger_before_query
# :nodoc:
protected def trigger_before_query
@before_query_triggers.each { |bq| bq.call }
@before_query_triggers.clear
end
Expand Down
8 changes: 5 additions & 3 deletions src/clear/sql/query/change.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module Clear::SQL::Query::Change
# Call back called when the query is changed
# Just here for being reimplemented
# (e.g. by collection for caching purpose)
# This method is called everytime the request has been changed
# By default, this do nothing and return `self`. However, it can be
# reimplemented to change some behavior when the query is changed
#
# (eg. it is by `Clear::Model::Collection`, to discard cache over collection)
def change! : self
self
end
Expand Down
3 changes: 3 additions & 0 deletions src/clear/sql/query/connection.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
module Clear::SQL
module Query::Connection
# Connection used by the query.
# Change it using `use_connection` method
getter connection_name : String = "default"

# Change the connection used by the query on execution
def use_connection(@connection_name : String)
self
end
Expand Down
18 changes: 18 additions & 0 deletions src/clear/sql/query/cte.cr
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
# Allow usage of Common Table Expressions (CTE) in the query building
module Clear::SQL::Query::CTE
# :nodoc:
alias CTEAuthorized = Clear::SQL::SelectBuilder | String

getter cte : Hash(String, CTEAuthorized)

# Add a CTE to the query.
#
# ```crystal
# Clear::SQL.select.with_cte("full_year",
# "SELECT DATE(date)"
# "FROM generate_series(NOW() - INTERVAL '1 year', NOW(), '1 day'::interval) date")
# .select("*").from("full_year")
# # WITH full_year AS ( SELECT DATE(date) ... ) SELECT * FROM full_year;
# ```
def with_cte(name, request : CTEAuthorized)
cte[name] = request
change!
end

# Add a CTE to the query. Use NamedTuple convention:
# ```crystal
# Clear::SQL.select.with_cte(cte: "xxx")
# # WITH cte AS xxx SELECT...
# ```
def with_cte(tuple : NamedTuple)
tuple.each do |k, v|
cte[k.to_s] = v
end
change!
end

# :nodoc:
protected def print_ctes
if cte.any?
{"WITH ",
Expand Down
14 changes: 13 additions & 1 deletion src/clear/sql/query/execute.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
require "db"

module Clear::SQL::Query::Execute
def execute(connection_name : String = "default")
# Execute an operation without asking for return
# If an optional `connection_name` parameter is given, this will
# override the connection used.
#
# ```crystal
# # Apply a method from an extension on multiple database
#
# %(default secondary).each do |cnx|
# Clear::SQL.select("pg_shards('xxx')").execute(cnx)
# end
# ```
def execute(connection_name : String? = nil)
connection_name ||= self.connection_name
Clear::SQL.execute(connection_name, to_sql)
end
end
28 changes: 23 additions & 5 deletions src/clear/sql/query/fetch.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ module Clear::SQL::Query::Fetch
rs.close
end

# Use a cursor to fetch the data
# Fetch the data using CURSOR.
# This will prevent Clear to load all the data from the database into memory.
# This is useful if you need to retrieve and update a large dataset.
def fetch_with_cursor(count = 1_000, &block : Hash(String, ::Clear::SQL::Any) -> Void)
trigger_before_query

Expand Down Expand Up @@ -48,8 +50,7 @@ module Clear::SQL::Query::Fetch
end
end

# Get a scalar (EG count)
#
# Helpers to fetch a SELECT with only one row and one column return.
def scalar(type : T.class) forall T
trigger_before_query

Expand All @@ -60,10 +61,12 @@ module Clear::SQL::Query::Fetch
end
end

# Return the first line of the query as Hash(String, ::Clear::SQL::Any)
def first
limit(1).fetch(fetch_all: true) { |x| return x }
end

# Return an array with all the rows fetched.
def to_a : Array(Hash(String, ::Clear::SQL::Any))
trigger_before_query

Expand All @@ -80,10 +83,25 @@ module Clear::SQL::Query::Fetch
end

# Fetch the result set row per row
# `fetch_all` is helpful in transactional environment, so it stores
# the result and close the resultset before strating to dispatch the data
# `fetch_all` optional parameter is helpful in transactional environment, so it stores
# the result and close the resultset before starting to call yield over the data
# preventing creation of a new connection if you need to call SQL into the
# yielded block.
#
# ```crystal
# # This is wrong: The connection is still busy retrieving the users:
# Clear::SQL.select.from("users").fetch do |u|
# Clear::SQL.select.from("posts").where { u["id"] == posts.id }
# end
#
# # Instead, use `fetch_all`
# # Clear will store the value of the result set in memory
# # before calling the block, and the connection is now ready to handle
# # another query.
# Clear::SQL.select.from("users").fetch(fetch_all:true) do |u|
# Clear::SQL.select.from("posts").where { u["id"] == posts.id }
# end
# ```
def fetch(fetch_all = false, &block : Hash(String, ::Clear::SQL::Any) -> Void)
trigger_before_query

Expand Down
18 changes: 13 additions & 5 deletions src/clear/sql/query/with_pagination.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,30 @@ module Clear::SQL::Query::WithPagination
property total_entries : Int64? = nil
end

# Maybe this goes on the Collection?
# Enter pagination mode.
# This is helpful to manage paginated table.
# Pagination will handle the page progression automatically and update
# `offset` and `limit` parameters by his own.
#
# ```crystal
# page = query.paginate(2, 50)
# ```
def paginate(page : Int32 = DEFAULT_PAGE, per_page : Int32 = DEFAULT_LIMIT)
# Need to clear these values to get total count first
clear_limit.clear_offset
@total_entries = count

# Calculate proper offset and set limit
page = page < 1 ? 1 : page
@limit = per_page.to_i64
@offset = (per_page * (page - 1)).to_i64
change!
end

# Return the number of items maximum per page.
def per_page
limit
end

# Return the current page
def current_page
if offset.nil? || limit.nil?
1
Expand All @@ -31,6 +38,7 @@ module Clear::SQL::Query::WithPagination
end
end

# Return the total number of pages.
def total_pages
if limit.nil? || total_entries.nil?
1
Expand All @@ -39,12 +47,12 @@ module Clear::SQL::Query::WithPagination
end
end

# current_page - 1 or nil if there is no previous page
# Return `current_page - 1` or `nil` if there is no previous page
def previous_page
current_page > 1 ? (current_page - 1) : nil
end

# current_page + 1 or nil if there is no next page
# Return `current_page + 1` or `nil` if there is no next page
def next_page
current_page < total_pages ? (current_page + 1) : nil
end
Expand Down

0 comments on commit 474d7d1

Please sign in to comment.