Since we had this problem for ~5 different machines, the pain of the accepted answer was not worth the gain. Instead, we went for an obvious but less-efficient solution...
Just copy the file over to a local designated drive like \\MachineName\Upload before each bulk insert. This location is assumed to exist on each of the 5 servers.
Since most of our bulk inserts are called via a C# API that we control, a BulkInsertHelper() class was created to use as a proxy. It is called by passing the original network location and proceeds by first testing a small test bulk insert using it. If that fails it then it falls back to the \\MachineName\Upload directory and retests.
If that succeeds, then it knows to copy the file before calling actual bulk insert.
Here is a simple way to check that bulk insert will have access to a file:
alter procedure [Common].[usp_FileExists] (
@pPathName nvarchar(300),
@poExists bit out,
@pDebug bit = 0
)
with execute as 'LoginThatIsInBulkAdminServerRole'
as
/*---------------------------------------------------------------------------------------------------------------------
Purpose: Return true if the file exists. Alternative to the xp_fileexists which may requires special permissions
for some users.
Modified By Description
---------- -------------- ---------------------------------------------------------------------------------------
2020.04.08 crokusek Initial version from udf_FileExists with added bulk insert test as a backup
---------------------------------------------------------------------------------------------------------------------*/
begin try
declare
@fileExists int;
exec xp_fileexist @pPathName, @fileExists output; -- may require syadmin priv so usually fails.
set @poExists = iif(coalesce(@fileExists, 0) = 1, 1, 0);
if (@poExists = 1)
return;
-- else retest using a bulk insert as the above returns false negatives on
-- network drives that aren't physically the local machine and missing instance permission.
begin try
declare
@sql varchar(1000) = '
declare @a bit = (
select 1
from openrowset(bulk ''' + @pPathName + ''', single_blob) f
);
';
execute (@sql)
set @poExists = 1; -- assume if no error that it worked
end try
begin catch
declare
@errorMessage nvarchar(max) = error_message();
if (@pDebug = 1)
print 'Caught bulk test error: ' + @errorMessage;
set @poExists = 0;
end catch
return @poExists;
end try
begin catch
declare
@CatchingUsp varchar(100) = object_name(@@procid);
if (xact_state() = -1)
rollback;
/* Adjust as needed
exec Common.usp_Log
@pMethod = @CatchingUsp;
exec Common.usp_RethrowError
@pCatchingMethod = @CatchingUsp; */
end catch
GO