Module: Sequel::SQLLogNormalizer

Defined in:
lib/sequel/extensions/sql_log_normalizer.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(db) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/sequel/extensions/sql_log_normalizer.rb', line 33

def self.extended(db)
  type = case db.literal("'")
  when "''''"
    :standard
  when "'\\''"
    :backslash
  when "N''''"
    :n_standard
  else
    raise Error, "SQL log normalization is not supported on this database (' literalized as #{db.literal("'").inspect})"
  end
  db.instance_variable_set(:@sql_string_escape_type, type)
end

Instance Method Details

#log_connection_yield(sql, conn, args = nil) ⇒ Object

Normalize the SQL before calling super.



48
49
50
51
52
53
54
# File 'lib/sequel/extensions/sql_log_normalizer.rb', line 48

def log_connection_yield(sql, conn, args=nil)
  unless skip_logging?
    sql = normalize_logged_sql(sql)
    args = nil
  end
  super
end

#normalize_logged_sql(sql) ⇒ Object

Replace literal strings and numbers in SQL with question mark placeholders.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/sequel/extensions/sql_log_normalizer.rb', line 57

def normalize_logged_sql(sql)
  sql = sql.dup
  sql.force_encoding('BINARY')
  start_index = 0
  check_n = @sql_string_escape_type == :n_standard
  outside_string = true

  if @sql_string_escape_type == :backslash
    search_char = /[\\']/
    escape_char_offset = 0
    escape_char_value = 92 # backslash
  else
    search_char = "'"
    escape_char_offset = 1
    escape_char_value = 39 # apostrophe
  end

  # The approach used here goes against Sequel's philosophy of never attempting
  # to parse SQL.  However, parsing the SQL is basically the only way to implement
  # this support with Sequel's design, and it's better to be pragmatic and accept
  # this than not be able to support this.

  # Replace literal strings
  while outside_string && (index = start_index = sql.index("'", start_index))
    if check_n && index != 0 && sql.getbyte(index-1) == 78 # N' start
      start_index -= 1
    end
    index += 1
    outside_string = false

    while (index = sql.index(search_char, index)) && (sql.getbyte(index + escape_char_offset) == escape_char_value)
      # skip escaped characters inside string literal
      index += 2
    end

    if index
      # Found end of string
      sql[start_index..index] = '?'
      start_index += 1
      outside_string = true
    end
  end

  # Replace integer and decimal floating point numbers
  sql.gsub!(/\b-?\d+(?:\.\d+)?\b/, '?')

  sql
end