Module: DbMod::Statements::Parameters

Defined in:
lib/db_mod/statements/parameters.rb

Overview

Parsing and validation of query parameters for prepared SQL statements

Constant Summary collapse

NUMBERED_PARAM =

Regex matching a numbered parameter

/\$\d+/
NAMED_PARAM =

Regex matching a named parameter

/\$[a-z]+(?:_[a-z]+)*/
NAMED_OR_NUMBERED =

For validation, named or numbered parameter

/^\$(?:\d+|[a-z]+(?:_[a-z]+)*)$/

Class Method Summary collapse

Class Method Details

.parameter_array(expected, args) ⇒ Array (private)

Convert the given named parameter hash into an array containing the parameter values in the order required to be supplied to the SQL statement being executed.

Parameters:

  • expected (Array<Symbol>)

    the parameters expected to be present

  • args (Hash)

    given parameters

Returns:

  • (Array)

    values to be passed to the prepared statement



102
103
104
105
106
107
108
# File 'lib/db_mod/statements/parameters.rb', line 102

def self.parameter_array(expected, args)
  expected.map do |arg|
    fail(ArgumentError, "missing arg #{arg}") unless args.key? arg

    args[arg]
  end
end

.parse_named_params!(sql, params) ⇒ Array<Symbol> (private)

Replaces the given list of named parameters in the query string with numbered parameters, and returns an array of symbols giving the order the parameters should be fed into the prepared statement for execution.

Parameters:

  • sql (String)

    the SQL statement. Will be modified.

  • params (Array<String>)

    ‘$one’, ‘$two’, etc…

Returns:

  • (Array<Symbol>)

    unique list of named parameters



145
146
147
148
149
150
151
152
153
# File 'lib/db_mod/statements/parameters.rb', line 145

def self.parse_named_params!(sql, params)
  unique_params = params.uniq
  params.each do |param|
    index = unique_params.index(param)
    sql[param] = "$#{index + 1}"
  end

  unique_params.map { |p| p[1..-1].to_sym }
end

.parse_numbered_params!(params) ⇒ Fixnum (private)

Validates the numbered parameters given (i.e. no gaps), and returns the parameter count.

Parameters:

  • params (Array<String>)

    ‘$1’,‘$2’, etc…

Returns:

  • (Fixnum)

    parameter count



126
127
128
129
130
131
132
133
134
135
# File 'lib/db_mod/statements/parameters.rb', line 126

def self.parse_numbered_params!(params)
  params.sort!
  params.uniq!
  if params.last[1..-1].to_i != params.length ||
     params.first[1..-1].to_i != 1
    fail ArgumentError, 'Invalid parameter list'
  end

  params.length
end

.parse_params!(sql) ⇒ Fixnum, Array<Symbol>

Called when a DbMod dynamically defined method is declared. Parses parameters, named or numbered, from an SQL statement. See the DbMod::Statements::Prepared module documentation for more. This method may modify the sql statement to change named parameters to numbered parameters. If the query uses numbered parameters, an integer will be returned that is the arity of the statement. If the query uses named parameters, an array of symbols will be returned, giving the order in which the named parameters should be fed into the statement.

Parameters:

  • sql (String)

    statement to prepare

Returns:

  • (Fixnum, Array<Symbol>)

    description of prepared statement’s parameters



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/db_mod/statements/parameters.rb', line 54

def self.parse_params!(sql)
  Parameters.valid_sql_params! sql
  numbered = sql.scan NUMBERED_PARAM
  named = sql.scan NAMED_PARAM

  if numbered.any?
    fail ArgumentError, 'mixed named and numbered params' if named.any?
    Parameters.parse_numbered_params! numbered
  else
    Parameters.parse_named_params! sql, named
  end
end

.valid_fixed_args!(count, args) ⇒ Object

Called when a DbMod dynamically defined method is called. Assert that the correct number of arguments has been provided.

Parameters:

  • count (Fixnum)

    arity of the method being called.

  • args (Array)

    list of arguments given.



33
34
35
36
37
# File 'lib/db_mod/statements/parameters.rb', line 33

def self.valid_fixed_args!(count, args)
  unless args.size == count
    fail ArgumentError, "#{args.size} args given, #{count} expected"
  end
end

.valid_named_args!(expected, args) ⇒ Array

Called when a DbMod dynamically defined method is called. Assert that the named arguments given for the prepared statement with the given name satisfy expectations. Returns a parameter array as per parameter_array.

Parameters:

  • expected (Array<Symbol>)

    the parameters expected to be present

  • args (Array<Hash<Symbol>>)

    arguments given to the method being executed. The method should only be called with an options hash that contains exactly the parameter names given when the method was defined.

Returns:

  • (Array)

    values to be passed to the prepared statement



17
18
19
20
21
22
23
24
25
26
# File 'lib/db_mod/statements/parameters.rb', line 17

def self.valid_named_args!(expected, args)
  wrapped_hash! args

  args = args.first
  if args.size != expected.size
    fail ArgumentError, "#{args.size} args given, #{expected.size} needed"
  end

  parameter_array(expected, args)
end

.valid_sql_params!(sql) ⇒ Object (private)

Fails if any parameters in an sql query aren’t in the expected format. They must either be lower_case_a_to_z or digits only.



113
114
115
116
117
118
119
# File 'lib/db_mod/statements/parameters.rb', line 113

def self.valid_sql_params!(sql)
  sql.scan(/\$[A-Za-z0-9_]+/) do |param|
    unless param =~ NAMED_OR_NUMBERED
      fail ArgumentError, "Invalid parameter #{param}"
    end
  end
end

.wrapped_hash!(args) ⇒ Object (private)

Assert that the given parameter list is an array containing a single hash of named parameter values.

Raises ArgumentError otherwise.

Parameters:

  • args (Array<Hash<Symbol>>)

    method arguments being validated



84
85
86
87
88
89
90
91
92
# File 'lib/db_mod/statements/parameters.rb', line 84

def self.wrapped_hash!(args)
  unless args.size == 1
    fail ArgumentError, "unexpected arguments: #{args.inspect}"
  end

  unless args.first.is_a? Hash
    fail ArgumentError, "invalid argument: #{args.first.inspect}"
  end
end