sp_send_dbmail executed from job fails with query result attached as file - sql-server

I have faced with the following issue: when trying to send email with results of query attached as file, using sp_send_dbmail via executing ordinary query everything seems to be working OK.
But if add the same code into JobStep and run the job, it fails.
Error in job history says
Error formatting query, probably invalid parameters [SQLSTATE 42000] (Error 22050). The step failed.
But when I comment out parameter that refers to file attaching it starts working correctly again.
exec msdb.dbo.sp_send_dbmail
#profile_name = 'profile_name',
#recipients = 'some#mail.com',
#body = 'body',
#subject = 'subj',
--Parameters that refers to attached file
#attach_query_result_as_file = 1,
#query_result_header = 0,
#query_result_no_padding = 1,
#query = 'select 1',
#query_attachment_filename = 'test.csv'
Any suggestions?

I've come to workaround of that issue. Don't know why would it work but never the less. :)
It is definitely about security.
I've investigated that SQL Agent is running on behalf of domain user, say DOMAIN\User.
It has full set of admin rights on server ('sysadmin' server role, etc). SQL Server itself is running under that same user.
The step of job that contains call to sp_send_dbmail runs under the same DOMAIN\User.
Also I've traced that when running the query part of sp_send_dbmail it tries to execute
exec xp_logininfo 'DOMAIN\User' to check against Active Directory if that user is OK. And surprise: something is definitely not OK. This check ends up with:
Msg 15404, Level 16, State 19, Server SQLC002INS02\SQLC002INS02, Line 1
Could not obtain information about Windows NT group/user 'DOMAIN\User.', error code 0x2.
That, with some probability can mean anything about that user's password is expired or user is locked or any other non pleasant things for that guy.
I decided that its to risky to change user for Agent. So I come up to sending mail on behalf of 'sa' which has same 'sysadmin' server role but SQL authorization and omits this AD checking step.
It looks like one user that pretends to be admin to ask the real admin to run dangerous code for him :)
So final code of this job's the first and the only step resembles this:
execute as login = 'sa'
exec msdb.dbo.sp_send_dbmail
#profile_name = 'profile_name',
#recipients = 'some#mail.com',
#body = 'body',
#subject = 'subj',
--Parameters that refers to attached file
#attach_query_result_as_file = 1,
#query_result_header = 0,
#query_result_no_padding = 1,
#query = 'select 1',
#query_attachment_filename = 'test.csv'
revert

I had this problem. I am using SQL Server 2008 R2. I got the email sent with more info about the error by adding option:
#append_query_error = 1,
I got the email with this error about permissions instead of my query:
Msg 916, Level 14, State 1, Server SERVER\INST01,
Procedure GetSalesReport, Line 62
The server principal "CONTROLLEDNETWO\sql.service" is not able
to access the database "MYDB01" under the current security co
ntext.
My query was trying to access some tables where SQL Agent had no permissions (actually in my case it has not even access to it).
I fixed it through SQLSMS by adding a new user "CONTROLLEDNETWO\sql.service" to the db "MYDB01" and granting permissions to "select".

This was all helpful thank you. Wanted to share what I was trying to do with the excel(xls) attachment which was put the results in columns. This worked for me by adding the query_result_no_padding = 1, and query_result_separator= ' , '. ( that is a Tab,Tab in the ticks )
#query_result_header= 1,
#attach_query_result_as_file = 1,
#query_result_no_padding = 1,
#query_attachment_filename = 'TestPriceFlingerReport.xls',
#query_result_separator= ' , ',
#profile_name = 'Test Exchange Server'

EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Main Profile',
#recipients = 'me#vwp.com',
#subject = 'Test',
#body = 'this is a test',
#execute_query_database = 'myTargetDatabase_mscrm',
#query = N'SELECT * from myTargetDatabase_mscrm.dbo.SystemUserBase',
#attach_query_result_as_file = 1,
#query_attachment_filename = 'Test.txt'
For reference, this failed repeatedly showing as invoked as the domain administrator, but run as local\sqladmin. After flipping variables off an on and trying to give permissions, I saw in the script of the job that it was still using the master database. I found the setting staring me in the face. It's in the configuration for the Step. I changed it to msdb and it worked. Keep in mind that I changed the select from myTable to select from myDatabase.dbo.myTable based on some posts. That may or may not have contributed to fixing the problem. I also used #execute_query_database to make sure it's running the query from the right place. Again, that might not have been necessary.
No matter what finally made it happy, it had nothing to do with whether it was attaching or not.

in my situation, It could not identify the table belongs to with database. Once the database.dbo.table was added to the query it worked.

When you manually execute your query, YOUR credentials are used. When SQL Agent executes the same query, the SQL Agent service account's credentials are used. By default, SQL Server Agent will use the LocalSystem account credentials. One way to fix the problem would be to change the user under which the SQL Server Agent service is running with a user that has access to your csv directory\file.

I believe this problem was due to a change implemented in SQL 2008 and later regarding locking down security for just the sp_send_dbmail.
It only happens if you pass a qry to send_dbmail to execute, and return the results thru the email. The problem is the error message is misleading and not appropriate.
A good solution is to create a SQL user with just the minimum necessary permissions to perform that query. For example, db_reader, or db_writer, and db_owner if absolutely necessary. And make that user the owner. You can also create a SQL credential and configure that sql job to run under that SQL credential.

I had this problem too, and solved it in two parts using much of the advice here.
1) Right-click, 'View History' on the job showed failure details, and the failure notice gave the name of the user the job ran under, so I gave this user read-only access to my DB.
2) I had forgotten to specify DBName.dbo.MyTableName and was using MyTableName only.
Incidentally, the emails were all going to my junk email folder.

Related

SQL Server - You do not have permission to use the bulk load statement

Using SQL Server 2016, I am trying to configure a user other than 'SA' to import a file. The code I am executing is as follows:
EXECUTE AS USER = 'DataImports';
SELECT CURRENT_USER;
EXEC xp_cmdshell 'TYPE myFileNameHere.txt'
BULK INSERT DataImports.staging_AddressBook
FROM 'myFileNameHere.txt'
WITH (DATAFILETYPE = 'char'
, FIRSTROW = 2
, FIELDTERMINATOR = ' '
, ROWTERMINATOR = '\n');
The error that I get is:
Msg 4834, Level 16, State 1, Line 20
You do not have permission to use the bulk load statement.
I have validated the following:
I do have access to the file as the user required - The cmdshell TYPE returns the rows expected. I do not appear to have a file access issue.
I have INSERT permission on the database in general.
I tested by using:
SELECT
[DatabaseUserName] = princ.[name],
[PermissionType] = perm.[permission_name],
[PermissionState] = perm.[state_desc]
FROM
sys.database_principals princ
LEFT JOIN
sys.database_permissions perm ON perm.[grantee_principal_id] = princ.[principal_id]
WHERE
princ.[name] = 'DataImports';`
I do have the bulk admin role
SELECT
r.name AS [RoleName],
m.name AS [MemberName],
CASE
WHEN m.name IS NOT NULL THEN 1 ELSE 0
END AS IsMember
FROM
sys.server_principals r
LEFT JOIN
sys.server_role_members rm ON (r.principal_id = rm.role_principal_id)
LEFT JOIN
sys.server_principals m ON (rm.member_principal_id = m.principal_id)
WHERE
r.type = 'R' AND m.name = 'Dataimports';
I have even configured the user to be a sys-admin (not part of the long term plan) but I'm still getting the error.
These are the main points that have been highlighted in the other SO tickets and general searches I have performed. I can import the table as SA but not as DataImports despite what appears to be correct configuration.
This is part of a job that is being run and currently we are having to give SA access just to read a file. Security wise this is less than ideal but I cannot work out what is missing.
Any suggestions of what else to check would be gratefully received - all the basics seem to be in place.
Any suggestions
of what else to check would be gratefully received - all the basics
seem to be in place.
Few things:
GRANT ADMINISTER BULK OPERATIONS TO Dataimports
If the destination table contains triggers or checks constraints
GRANT ALTER ON TABLE DataImports.staging_AddressBook TO Dataimports
And
ALTER DATABASE [yourDB] SET TRUSTWORTHY ON;
Because of:
For security considerations, the server-scoped permissions are
stripped down when you impersonate a database user unless the system
administrator has explicitly set SQL Server to trust the impersonated
context at the server-scope. In this case, a login with the control
server server-scoped permission has no permissions to access any
particular database. Therefore, the trigger module that is executed as
this login cannot run.

SQL Server Log out / Terminate session of logged in user in a query

Without going into why I would like to do this, is it possible (I'll be using a login trigger) to log out a user that has no write permissions to a certain database?
I am able to find the currently logged in users permission, I just need to know if it's possible to log them out?
DECLARE #HasPermission bit
SELECT #HasPermission = HAS_PERMS_BY_NAME('RTEST2.dbo.TestTableSize', 'OBJECT', 'INSERT');
IF #HasPermission = 0
SELECT 'Now this is where id want to log out the user'
One can prevent a user from logging in by executing a ROLLBACK from within a login trigger. As #DavidBrowneMicrosoft mentioned in his comment, it's also a good practice to use a PRINT or RAISERROR statement so that reason for the login failure is logged. This message will not be returned to the client but may be useful for troubleshooting.
IF #HasPermission = 0
BEGIN
PRINT 'User does not have permissions to login';
ROLLBACK;
END;

Litespeed error : Table name must be specified in the format owner_name.table_name

I'm trying to recover a table from Litespeed bakcup. The table is of schema SOURCE. Litespeed object recovery wizard fails with the error:: Table name must be specified in the format owner_name.table_name. I tried with the store procedure directly as well but it's giving the same error. Please help me fix this issue:
EXEC master.dbo.xp_objectrecovery
#filename = 'backup_file_name'
, #filenumber = 1
, #objectname = 'SOURCE.target_rpt_2016'
, #destinationdatabase = 'database_name'
,#destinationtable ='SOURCE.target_rpt_2016_restore'
, #tempdirectory = 'recovery_temp_dir'
I tried giving destinationtable without schema/dbo as well but it's throwing same error.
Atlast figured out the issue.
The owner of the schema Source is a domain account Dom\AXp0101. So when I changed the paramter #ObjectName to '[Dom\AXp0101].[source].[2016_target_rpt_2016]' the recovery completed. Read somewhere that as the owner of this particular schema is a domain account, there might be issues associated with demiliters so we have exclusively specify like above.

In msdb.dbo.sp_send_dbmail, #profile_name is not valid

I use a SQL Server job to execute SendEmail stored procedure. The job is working fine when the #recipients is our specified email address.
Since I couldn't log into our specified email address to check whether the email has been received, I want to change #recipients as my email address
The question is when I make this changed, the job history shows this job is not executed. The error is
Profile_name is not valid
Does anyone have any idea about this?
Here is the example.
EXEC msdb.dbo.sp_send_email
#profile_name = 'ISSS Group',
#recipients = 'isss#xxx.edu',
#body = #mess,--#mess is a table
#body_format = 'HTML',
#subject = 'Student Information Update',
#from_address = 'administration#xxx.edu';
I want to change #recipients = 'isss#xxx.edu' to #recipients = 'myemail#xxx.edu'to check whether the email has been received. But once I change it, the SQL Server job is failed. The error shows
Profile name is not valid.
You must pass the name of a registered profile; you can't make them up on-the-fly.
However, I assume you want a different display name, hence why you seem to be trying to change it by renaming the profile.
You would either need to create a new profile for the new name, or simply pass it in at runtime. The format for this is:
#from_address = 'Displayed Name <emailaddress#example.com>'
So, for example,
#from_address = 'ISSS Group <administration#xxx.edu>'
I use this method, and it might do everything you need here. Whether there is any benefit to setting up an entirely separate profile, I don't know; you would have to research that further.

Intellisense and context help for sys stored procedures - bugs or by design?

SQL Server 2008 R2 Dev
Execution in SSMS of:
1)
use AdventureWorksDW;
GO;
sp_cdc_enable_table
'dbo',
'FactInternetSales',
#role_name=NULL,
#supports_net_changes=0
succeeds.
Why does execution of
2)
sp_cdc_enable_table 'dbo', 'FactInternetSales' --, #role_name=NULL, #supports_net_changes=0
gives error:
Msg 201, Level 16, State 4, Procedure sp_cdc_enable_table, Line 0
Procedure or function 'sp_cdc_enable_table' expects parameter '#role_name', which was not supplied.
while Intellisense popup on (having mouse cursor on) sp_cdc_enable_table in SSMS shows:
stored procedure AdventureWorksDW.sys.sp_cdc_enable_table
#source_schema sysname,
#source_name sysname,
#capture_instance sysname = null,
#supports_net_changes bit = null,
#role_name sysname,
#index_name sysname =null,
[continuation truncated by vgv8]
Does not " = null" imply default value which is used if parameter is omitted?
Why does not ommission of #index_name sysname give the error?
Collateral questions:
How can I copy the text of popup description into buffer (for further pasting)?
Is not "#role_name bit = null" in popup an error (and should have been "#role_name bit = 0, " instead)?
Update:
I corrected the typo in post.
Really I executed
sp_cdc_enable_table 'dbo', 'FactInternetSales'--, #role_name=NULL, #supports_net_changes=0
i.e. both variants are execution of the same script
1) uncommented
2) with comments
Update3:
OK, initially I typed incorrectly the text of popup which I corrected now.
So, the question about error is removed, thanks.
From the context of my successful execution of 1) you are supposed to notice that my questions are about popup (but not how to succeed):
whether the popup is (in)correct?
"#capture_instance sysname = null, should not it be enclosed in brackets [ ] showing that it can be ommitted?
"#supports_net_changes bit = null," - should not it be "= 0"?
how to copy popup (for example, for reporting a bug in Microsoft Connect)?
Notice that popup also has "#capture_instance = null," which can be completely ommitted but it is not marked this in any way.
Update4:
I included the screenshot.
Well, it is rather context sensitive help.
Intellisense does not work at all on sys.sp_* and popup is not shown if there is a syntax error - whatever MS pretended to accomplish by such "help" since it is necessary to insert full correct statement in order to escape syntax errors and have context sensitive "help"...
Update5:
Then, what is the sense that parameters with default values cannot be dropped?
You are missing a '
sp_cdc_enable_table 'dbo', FactInternetSales'
^ here
Change it to:
sp_cdc_enable_table 'dbo', 'FactInternetSales'
And it should fix your problem.
According to the MSDN documentation here, #role_name must be specified.
[ #role_name = ] 'role_name'
Is the name of the database role used to gate access to change data. role_name is sysname and must be specified. If explicitly set to NULL, no gating role is used to limit access to the change data.
If the role currently exists, it is used. If the role does not exist, an attempt is made to create a database role with the specified name. The role name is trimmed of white space at the right of the string before attempting to create the role. If the caller is not authorized to create a role within the database, the stored procedure operation fails.

Resources