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

[View source]

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.

[View source]

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.

[View source]

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