-
Notifications
You must be signed in to change notification settings - Fork 12
Feature/support prepared statements #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
andsel
wants to merge
1
commit into
logstash-plugins:master
from
andsel:feature/support_prepared_statements
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,6 +63,8 @@ def initialize(options, globals, default_id) | |
| @valid = false | ||
| @option_errors = [] | ||
| @default_result = nil | ||
| @prepared_statement = nil | ||
| @symbol_parameters = nil | ||
| parse_options | ||
| end | ||
|
|
||
|
|
@@ -79,8 +81,11 @@ def formatted_errors | |
| end | ||
|
|
||
| def enhance(local, event) | ||
| result = fetch(local, event) # should return a LookupResult | ||
|
|
||
| if @prepared_statement | ||
| result = call_prepared(local, event) | ||
| else | ||
| result = fetch(local, event) # should return a LookupResult | ||
| end | ||
| if result.failed? || result.parameters_invalid? | ||
| tag_failure(event) | ||
| end | ||
|
|
@@ -98,6 +103,17 @@ def enhance(local, event) | |
| end | ||
| end | ||
|
|
||
| def use_prepared_statement? | ||
| @prepared_parameters && !@prepared_parameters.empty? | ||
| end | ||
|
|
||
| def prepare(local) | ||
| hash = {} | ||
| @prepared_parameters.each_with_index { |v, i| hash[:"$p#{i}"] = v } | ||
| @prepared_param_placeholder_map = hash | ||
| @prepared_statement = local.prepare(query, hash.keys) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def tag_failure(event) | ||
|
|
@@ -139,6 +155,33 @@ def fetch(local, event) | |
| result | ||
| end | ||
|
|
||
| def call_prepared(local, event) | ||
| result = LookupResult.new() | ||
| if @parameters_specified | ||
| params = prepare_parameters_from_event(event, result) | ||
| if result.parameters_invalid? | ||
| logger.warn? && logger.warn("Parameter field not found in event", :lookup_id => @id, :invalid_parameters => result.invalid_parameters) | ||
| return result | ||
| end | ||
| else | ||
| params = {} | ||
| end | ||
| begin | ||
| logger.debug? && logger.debug("Executing Jdbc query", :lookup_id => @id, :statement => query, :parameters => params) | ||
| @prepared_statement.call(params).each do |row| | ||
| stringified = row.inject({}){|hash,(k,v)| hash[k.to_s] = v; hash} #Stringify row keys | ||
| result.push(stringified) | ||
| end | ||
| rescue ::Sequel::Error => e | ||
| # all sequel errors are a subclass of this, let all other standard or runtime errors bubble up | ||
| result.failed! | ||
| logger.warn? && logger.warn("Exception when executing Jdbc query", :lookup_id => @id, :exception => e.message, :backtrace => e.backtrace.take(8)) | ||
| end | ||
| # if either of: no records or a Sequel exception occurs the payload is | ||
| # empty and the default can be substituted later. | ||
| result | ||
| end | ||
|
|
||
| def process_event(event, result) | ||
| # use deep clone here so other filter function don't taint the payload by reference | ||
| event.set(@target, ::LogStash::Util.deep_clone(result.payload)) | ||
|
|
@@ -162,17 +205,34 @@ def parse_options | |
| @option_errors << "The options for '#{@id}' must include a 'query' string" | ||
| end | ||
|
|
||
| @parameters = @options["parameters"] | ||
| @parameters_specified = false | ||
| if @parameters | ||
| if [email protected]_a?(Hash) | ||
| @option_errors << "The 'parameters' option for '#{@id}' must be a Hash" | ||
| else | ||
| # this is done once per lookup at start, i.e. Sprintfier.new et.al is done once. | ||
| @symbol_parameters = @parameters.inject({}) {|hash,(k,v)| hash[k.to_sym] = sprintf_or_get(v) ; hash } | ||
| # the user might specify an empty hash parameters => {} | ||
| # maybe due to an unparameterised query | ||
| @parameters_specified = !@symbol_parameters.empty? | ||
| if @options["parameters"] && @options["prepared_parameters"] | ||
| @option_errors << "Can't specify 'parameters' and 'prepared_parameters' in the same lookup" | ||
| else | ||
| @parameters = @options["parameters"] | ||
| @prepared_parameters = @options["prepared_parameters"] | ||
| @parameters_specified = false | ||
| if @parameters | ||
| if [email protected]_a?(Hash) | ||
| @option_errors << "The 'parameters' option for '#{@id}' must be a Hash" | ||
| else | ||
| # this is done once per lookup at start, i.e. Sprintfier.new et.al is done once. | ||
| @symbol_parameters = @parameters.inject({}) {|hash,(k,v)| hash[k.to_sym] = sprintf_or_get(v) ; hash } | ||
| # the user might specify an empty hash parameters => {} | ||
| # maybe due to an unparameterised query | ||
| @parameters_specified = !@symbol_parameters.empty? | ||
| end | ||
| elsif @prepared_parameters | ||
| if !@prepared_parameters.is_a?(Array) | ||
| @option_errors << "The 'prepared_parameters' option for '#{@id}' must be an Array" | ||
| elsif @query.count("?") != @prepared_parameters.size | ||
| @option_errors << "The 'prepared_parameters' option for '#{@id}' doesn't match count with query's placeholder" | ||
| else | ||
| #prepare the map @symbol_parameters :n => sprintf_or_get | ||
| hash = {} | ||
| @prepared_parameters.each_with_index {|v,i| hash[:"p#{i}"] = sprintf_or_get(v)} | ||
| @symbol_parameters = hash | ||
| @parameters_specified = !@prepared_parameters.empty? | ||
| end | ||
| end | ||
| end | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: why do we need to stringify (just curious) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've taken suggestion from the no prepared part, I think the stringify is need to avoid errors of type conversion error when the JDBC driver loads a BigInteger or the driver does ugly conversions.