@@ -63,6 +63,8 @@ def initialize(options, globals, default_id)
6363 @valid = false
6464 @option_errors = [ ]
6565 @default_result = nil
66+ @prepared_statement = nil
67+ @symbol_parameters = nil
6668 parse_options
6769 end
6870
@@ -79,8 +81,11 @@ def formatted_errors
7981 end
8082
8183 def enhance ( local , event )
82- result = fetch ( local , event ) # should return a LookupResult
83-
84+ if @prepared_statement
85+ result = call_prepared ( local , event )
86+ else
87+ result = fetch ( local , event ) # should return a LookupResult
88+ end
8489 if result . failed? || result . parameters_invalid?
8590 tag_failure ( event )
8691 end
@@ -98,6 +103,17 @@ def enhance(local, event)
98103 end
99104 end
100105
106+ def use_prepared_statement?
107+ @prepared_parameters && !@prepared_parameters . empty?
108+ end
109+
110+ def prepare ( local )
111+ hash = { }
112+ @prepared_parameters . each_with_index { |v , i | hash [ :"$p#{ i } " ] = v }
113+ @prepared_param_placeholder_map = hash
114+ @prepared_statement = local . prepare ( query , hash . keys )
115+ end
116+
101117 private
102118
103119 def tag_failure ( event )
@@ -139,6 +155,33 @@ def fetch(local, event)
139155 result
140156 end
141157
158+ def call_prepared ( local , event )
159+ result = LookupResult . new ( )
160+ if @parameters_specified
161+ params = prepare_parameters_from_event ( event , result )
162+ if result . parameters_invalid?
163+ logger . warn? && logger . warn ( "Parameter field not found in event" , :lookup_id => @id , :invalid_parameters => result . invalid_parameters )
164+ return result
165+ end
166+ else
167+ params = { }
168+ end
169+ begin
170+ logger . debug? && logger . debug ( "Executing Jdbc query" , :lookup_id => @id , :statement => query , :parameters => params )
171+ @prepared_statement . call ( params ) . each do |row |
172+ stringified = row . inject ( { } ) { |hash , ( k , v ) | hash [ k . to_s ] = v ; hash } #Stringify row keys
173+ result . push ( stringified )
174+ end
175+ rescue ::Sequel ::Error => e
176+ # all sequel errors are a subclass of this, let all other standard or runtime errors bubble up
177+ result . failed!
178+ logger . warn? && logger . warn ( "Exception when executing Jdbc query" , :lookup_id => @id , :exception => e . message , :backtrace => e . backtrace . take ( 8 ) )
179+ end
180+ # if either of: no records or a Sequel exception occurs the payload is
181+ # empty and the default can be substituted later.
182+ result
183+ end
184+
142185 def process_event ( event , result )
143186 # use deep clone here so other filter function don't taint the payload by reference
144187 event . set ( @target , ::LogStash ::Util . deep_clone ( result . payload ) )
@@ -162,17 +205,34 @@ def parse_options
162205 @option_errors << "The options for '#{ @id } ' must include a 'query' string"
163206 end
164207
165- @parameters = @options [ "parameters" ]
166- @parameters_specified = false
167- if @parameters
168- if !@parameters . is_a? ( Hash )
169- @option_errors << "The 'parameters' option for '#{ @id } ' must be a Hash"
170- else
171- # this is done once per lookup at start, i.e. Sprintfier.new et.al is done once.
172- @symbol_parameters = @parameters . inject ( { } ) { |hash , ( k , v ) | hash [ k . to_sym ] = sprintf_or_get ( v ) ; hash }
173- # the user might specify an empty hash parameters => {}
174- # maybe due to an unparameterised query
175- @parameters_specified = !@symbol_parameters . empty?
208+ if @options [ "parameters" ] && @options [ "prepared_parameters" ]
209+ @option_errors << "Can't specify 'parameters' and 'prepared_parameters' in the same lookup"
210+ else
211+ @parameters = @options [ "parameters" ]
212+ @prepared_parameters = @options [ "prepared_parameters" ]
213+ @parameters_specified = false
214+ if @parameters
215+ if !@parameters . is_a? ( Hash )
216+ @option_errors << "The 'parameters' option for '#{ @id } ' must be a Hash"
217+ else
218+ # this is done once per lookup at start, i.e. Sprintfier.new et.al is done once.
219+ @symbol_parameters = @parameters . inject ( { } ) { |hash , ( k , v ) | hash [ k . to_sym ] = sprintf_or_get ( v ) ; hash }
220+ # the user might specify an empty hash parameters => {}
221+ # maybe due to an unparameterised query
222+ @parameters_specified = !@symbol_parameters . empty?
223+ end
224+ elsif @prepared_parameters
225+ if !@prepared_parameters . is_a? ( Array )
226+ @option_errors << "The 'prepared_parameters' option for '#{ @id } ' must be an Array"
227+ elsif @query . count ( "?" ) != @prepared_parameters . size
228+ @option_errors << "The 'prepared_parameters' option for '#{ @id } ' doesn't match count with query's placeholder"
229+ else
230+ #prepare the map @symbol_parameters :n => sprintf_or_get
231+ hash = { }
232+ @prepared_parameters . each_with_index { |v , i | hash [ :"p#{ i } " ] = sprintf_or_get ( v ) }
233+ @symbol_parameters = hash
234+ @parameters_specified = !@prepared_parameters . empty?
235+ end
176236 end
177237 end
178238
0 commit comments