Skip to content

Commit 5923ba0

Browse files
committed
(MODULES-6281) Return Errors from T-SQL
The sqlserver_tsql resource does not handle errors returned from T-SQL statements properly. The errors come back from the query but unless the phrase 'SQL Server' was included in the error text, the error was not then passed back to the user/logs. This change ensure that errors returned are passed back properly by inspecting the Errors property of the ADO object used to run the query. Note that this may not behave as expected if the error that occurrs is of sufficently low severity that a rowset, even if empty, is also returned. If a rowset is returned by the ADO object, the object will not populate the Errors property, and there is no way to tell a low severity error occurred. For instance 'Select 1/0' returns a low severity division by zero error, and an empty rowset. Because the empty rowset exists, the error cannot be seen. It is up to the user to ensure that the errors they want to catch are of sufficienty severity that execution of the query is halted.
1 parent dc2512a commit 5923ba0

4 files changed

Lines changed: 34 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
1616
- During acceptance testing, only execute master provisioning steps if there is
1717
a master in the hosts array.
1818
- Stop running ```gem update bundler``` during Travis runs. ([MODULES-6339](https://tickets.puppetlabs.com/browse/MODULES6339))
19+
- The `sqlserver_tsql` resource now returns errors from sql queries properly. ([MODULES-6281](https://tickets.puppetlabs.com/browse/MODULES-6281))
1920

2021
## [2.1.0] - 2017-12-8
2122

lib/puppet_x/sqlserver/sql_connection.rb

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ def open_and_run_command(query, config)
99
open(config)
1010
execute(query)
1111
rescue win32_exception => e
12-
return ResultOutput.new(true, e.message)
12+
# require 'pry'; binding.pry;
13+
return ResultOutput.new(true, e.message, @connection)
1314
ensure
1415
close
1516
end
1617

17-
ResultOutput.new(false, nil)
18+
# require 'pry'; binding.pry;
19+
20+
ResultOutput.new(false, nil, @connection)
1821
end
1922

2023
private
@@ -90,24 +93,25 @@ def win32_exception
9093
end
9194

9295
class ResultOutput
93-
attr_reader :exitstatus, :error_message, :raw_error_message
9496

95-
def initialize(has_errors, error_message)
97+
attr_reader :exitstatus, :error_message
98+
99+
def initialize(has_errors, error_message, connection)
96100
@exitstatus = has_errors ? 1 : 0
97-
if error_message
98-
@raw_error_message = error_message
99-
@error_message = parse_for_error(error_message)
100-
end
101+
102+
@error_message = (extract_messages(connection) || error_message)
101103
end
102104

103-
def has_errors
104-
@exitstatus != 0
105+
def extract_messages(connection)
106+
return nil if connection.nil? || connection.Errors.count == 0
107+
108+
error_count = connection.Errors.count - 1
109+
110+
((0..error_count).map { |i| connection.Errors(i).Description.to_s}).join("\n")
105111
end
106112

107-
private
108-
def parse_for_error(result)
109-
match = result.match(/SQL Server\n\s*(.*)/i)
110-
match[1] unless match == nil
113+
def has_errors
114+
@exitstatus != 0
111115
end
112116
end
113117
end

spec/acceptance/sqlserver_tsql_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def ensure_sqlserver_database(db_name, ensure_val = 'present')
191191
}
192192
MANIFEST
193193
execute_manifest(pp, {:acceptable_exit_codes => [0,1]}) do |r|
194-
expect(r.stderr).to match(/Error/i)
194+
expect(r.stderr).to match(/Incorrect syntax/i)
195195
end
196196
end
197197

@@ -210,7 +210,7 @@ def ensure_sqlserver_database(db_name, ensure_val = 'present')
210210
}
211211
MANIFEST
212212
execute_manifest(pp, {:acceptable_exit_codes => [0,1]}) do |r|
213-
expect(r.stderr).to match(/Error/i)
213+
expect(r.stderr).to match(/Non-Existing-Database/i)
214214
end
215215
end
216216
end

spec/unit/puppet_x/sql_connection_spec.rb

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88

99
def stub_connection
1010
@connection = mock()
11+
error_mock = mock()
12+
error_mock.stubs(:Description).returns("SQL Error in Connection")
13+
error_mock.stubs(:count).returns(0)
14+
1115
subject.stubs(:create_connection).returns(@connection)
1216
@connection.stubs(:State).returns(PuppetX::Sqlserver::CONNECTION_CLOSED)
17+
@connection.stubs(:Errors).returns(error_mock)
1318
subject.stubs(:win32_exception).returns(Exception)
1419
end
1520

@@ -20,11 +25,11 @@ def stub_connection
2025
@connection.stubs(:Open).with('Provider=SQLNCLI11;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;User ID=sa;Password=Pupp3t1@')
2126
end
2227
it 'should not raise an error but populate has_errors with message' do
23-
subject.stubs(:execute).raises(Exception.new("SQL Server\n error has happened"))
28+
@connection.Errors.stubs(:count).returns(1)
2429
expect {
2530
result = subject.open_and_run_command('whacka whacka whacka', config)
2631
expect(result.exitstatus).to eq(1)
27-
expect(result.error_message).to eq('error has happened')
32+
expect(result.error_message).to eq('SQL Error in Connection')
2833
}.to_not raise_error(Exception)
2934

3035
end
@@ -114,20 +119,20 @@ def stub_connection
114119
end
115120
context 'return result with errors' do
116121
it {
117-
subject.stubs(:win32_exception).returns(Exception)
122+
stub_connection
123+
@connection.Errors.stubs(:count).returns(1)
124+
@connection.stubs(:Execute).raises(Exception)
118125
subject.expects(:open).with({:admin_user => 'sa', :admin_pass => 'Pupp3t1@', :instance_name => 'MSSQLSERVER'})
119-
subject.expects(:execute).with('SELECT * FROM sys.databases').raises(Exception.new("SQL Server\ninvalid syntax provider"))
120126
subject.expects(:close).once
121-
result =
122-
subject.open_and_run_command('SELECT * FROM sys.databases', config)
127+
result = subject.open_and_run_command('SELECT * FROM sys.databases', config)
123128
expect(result.exitstatus).to eq(1)
124-
expect(result.error_message).to eq('invalid syntax provider')
129+
expect(result.error_message).to eq('SQL Error in Connection')
125130
}
126131
end
127132
context 'open connection failure' do
128133
it {
129134
stub_connection
130-
err_message = "SQL Server\n ConnectionFailed"
135+
err_message = "ConnectionFailed"
131136
@connection.stubs(:Open).raises(Exception.new(err_message))
132137
expect {
133138
result = subject.open_and_run_command('whacka whacka whacka', config)

0 commit comments

Comments
 (0)