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

Raises:

  • (ArgumentError)

    if any arguments are missing



108
109
110
111
112
113
114
# File 'lib/db_mod/statements/parameters.rb', line 108

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



158
159
160
161
162
163
164
165
166
# File 'lib/db_mod/statements/parameters.rb', line 158

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

Raises:

  • (ArgumentError)

    if there are any problems with the given argument list



139
140
141
142
143
144
145
146
147
148
# File 'lib/db_mod/statements/parameters.rb', line 139

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

Raises:

  • (ArgumentError)

    if there is any sort of problem with the parameters declared in the sql statement



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/db_mod/statements/parameters.rb', line 57

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.

Raises:

  • (ArgumentError)

    if the wrong number of arguments is given



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

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.

Parameters:

  • sql (String)

    sql statement that may or may not contain parameters

Raises:

  • (ArgumentError)

    if there are any invalid parameter declarations



124
125
126
127
128
129
130
# File 'lib/db_mod/statements/parameters.rb', line 124

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

Raises:

  • (ArgumentError)

    if the arguments passed to the method do not pass validation



89
90
91
92
93
94
95
96
97
# File 'lib/db_mod/statements/parameters.rb', line 89

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