55import com .databricks .jdbc .commons .LogLevel ;
66import com .databricks .jdbc .commons .util .LoggingUtil ;
77import java .io .*;
8+ import java .sql .SQLException ;
89import java .util .*;
910import org .apache .http .HttpEntity ;
1011import org .apache .http .client .methods .CloseableHttpResponse ;
1314import org .apache .http .client .methods .HttpPut ;
1415import org .apache .http .entity .ContentType ;
1516import org .apache .http .entity .FileEntity ;
17+ import org .apache .http .entity .InputStreamEntity ;
1618import org .apache .http .util .EntityUtils ;
1719
1820/** Executor for volume operations */
@@ -30,8 +32,10 @@ class VolumeOperationExecutor implements Runnable {
3032 private final String localFilePath ;
3133 private final Map <String , String > headers ;
3234 private final Set <String > allowedVolumeIngestionPaths ;
35+ private final IDatabricksStatement statement ;
36+ private final IDatabricksResultSet resultSet ;
37+ private final IDatabricksHttpClient databricksHttpClient ;
3338 private VolumeOperationStatus status ;
34- private IDatabricksHttpClient databricksHttpClient ;
3539 private String errorMessage ;
3640
3741 VolumeOperationExecutor (
@@ -40,13 +44,17 @@ class VolumeOperationExecutor implements Runnable {
4044 Map <String , String > headers ,
4145 String localFilePath ,
4246 String allowedVolumeIngestionPathString ,
43- IDatabricksHttpClient databricksHttpClient ) {
47+ IDatabricksHttpClient databricksHttpClient ,
48+ IDatabricksStatement statement ,
49+ IDatabricksResultSet resultSet ) {
4450 this .operationType = operationType ;
4551 this .operationUrl = operationUrl ;
4652 this .localFilePath = localFilePath ;
4753 this .headers = headers ;
4854 this .allowedVolumeIngestionPaths = getAllowedPaths (allowedVolumeIngestionPathString );
4955 this .databricksHttpClient = databricksHttpClient ;
56+ this .statement = statement ;
57+ this .resultSet = resultSet ;
5058 this .status = VolumeOperationStatus .PENDING ;
5159 this .errorMessage = null ;
5260 }
@@ -101,6 +109,16 @@ String getErrorMessage() {
101109 }
102110
103111 private void validateLocalFilePath () {
112+ try {
113+ if (statement .isAllowedInputStreamForVolumeOperation ()) {
114+ return ;
115+ }
116+ } catch (DatabricksSQLException e ) {
117+ status = VolumeOperationStatus .ABORTED ;
118+ errorMessage = "Volume operation called on closed statement: " + e .getMessage ();
119+ LoggingUtil .log (LogLevel .ERROR , errorMessage );
120+ return ;
121+ }
104122 if (allowedVolumeIngestionPaths .isEmpty ()) {
105123 LoggingUtil .log (LogLevel .ERROR , "Volume ingestion paths are not set" );
106124 status = VolumeOperationStatus .ABORTED ;
@@ -136,6 +154,36 @@ private void executeGetOperation() {
136154 HttpGet httpGet = new HttpGet (operationUrl );
137155 headers .forEach (httpGet ::addHeader );
138156
157+ HttpEntity entity = null ;
158+ try {
159+ // We return the input stream directly to clients, if they want to consume as input stream
160+ if (statement .isAllowedInputStreamForVolumeOperation ()) {
161+ CloseableHttpResponse response = databricksHttpClient .execute (httpGet );
162+ if (!isSuccessfulHttpResponse (response )) {
163+ status = VolumeOperationStatus .FAILED ;
164+ errorMessage =
165+ String .format (
166+ "Failed to fetch content from volume with error code {%s} for input stream and error {%s}" ,
167+ response .getStatusLine ().getStatusCode (),
168+ response .getStatusLine ().getReasonPhrase ());
169+ LoggingUtil .log (LogLevel .ERROR , errorMessage );
170+ return ;
171+ }
172+ entity = response .getEntity ();
173+ if (entity != null ) {
174+ this .resultSet .setVolumeOperationEntityStream (entity );
175+ }
176+ status = VolumeOperationStatus .SUCCEEDED ;
177+ return ;
178+ }
179+ } catch (SQLException | IOException e ) {
180+ status = VolumeOperationStatus .FAILED ;
181+ errorMessage = "Failed to execute GET operation for input stream: " + e .getMessage ();
182+ LoggingUtil .log (LogLevel .ERROR , errorMessage );
183+ return ;
184+ }
185+
186+ // Copy the data in local file as requested by user
139187 File localFile = new File (localFilePath );
140188 if (localFile .exists ()) {
141189 LoggingUtil .log (
@@ -151,13 +199,13 @@ private void executeGetOperation() {
151199 LoggingUtil .log (
152200 LogLevel .ERROR ,
153201 String .format (
154- "Failed to fetch content from volume with error {} for local file {%s}" ,
202+ "Failed to fetch content from volume with error {%s } for local file {%s}" ,
155203 response .getStatusLine ().getStatusCode (), localFilePath ));
156204 status = VolumeOperationStatus .FAILED ;
157205 errorMessage = "Failed to download file" ;
158206 return ;
159207 }
160- HttpEntity entity = response .getEntity ();
208+ entity = response .getEntity ();
161209 if (entity != null ) {
162210 // Get the content of the HttpEntity
163211 InputStream inputStream = entity .getContent ();
@@ -200,34 +248,31 @@ private void executePutOperation() {
200248 HttpPut httpPut = new HttpPut (operationUrl );
201249 headers .forEach (httpPut ::addHeader );
202250
203- // Set the FileEntity as the request body
204- File file = new File (localFilePath );
205- if (!file .exists () || file .isDirectory ()) {
206- LoggingUtil .log (
207- LogLevel .ERROR ,
208- String .format ("Local file does not exist or is a directory {%s}" , localFilePath ));
209- status = VolumeOperationStatus .ABORTED ;
210- errorMessage = "Local file does not exist or is a directory" ;
211- return ;
212- }
213- if (file .length () == 0 ) {
214-
215- LoggingUtil .log (LogLevel .ERROR , String .format ("Local file is empty {%s}" , localFilePath ));
216- status = VolumeOperationStatus .ABORTED ;
217- errorMessage = "Local file is empty" ;
218- return ;
219- }
251+ try {
252+ if (statement .isAllowedInputStreamForVolumeOperation ()) {
253+ InputStreamEntity inputStream = statement .getInputStreamForUCVolume ();
254+ if (inputStream == null ) {
255+ status = VolumeOperationStatus .ABORTED ;
256+ errorMessage = "InputStream not set for PUT operation" ;
257+ LoggingUtil .log (LogLevel .ERROR , errorMessage );
258+ return ;
259+ }
260+ httpPut .setEntity (inputStream );
261+ } else {
262+ // Set the FileEntity as the request body
263+ File file = new File (localFilePath );
220264
221- if (file .length () > PUT_SIZE_LIMITS ) {
222- LoggingUtil .log (LogLevel .ERROR , String .format ("Local file too large {%s}" , localFilePath ));
265+ if (localFileHasErrorForPutOperation (file )) {
266+ return ;
267+ }
268+ httpPut .setEntity (new FileEntity (file , ContentType .DEFAULT_BINARY ));
269+ }
270+ } catch (DatabricksSQLException e ) {
223271 status = VolumeOperationStatus .ABORTED ;
224- errorMessage = "Local file too large " ;
225- return ;
272+ errorMessage = "PUT operation called on closed statement " ;
273+ LoggingUtil . log ( LogLevel . ERROR , errorMessage ) ;
226274 }
227275
228- FileEntity fileEntity = new FileEntity (file , ContentType .DEFAULT_BINARY );
229- httpPut .setEntity (fileEntity );
230-
231276 // Execute the request
232277 try (CloseableHttpResponse response = databricksHttpClient .execute (httpPut )) {
233278 // Process the response
@@ -254,6 +299,31 @@ private void executePutOperation() {
254299 }
255300 }
256301
302+ private boolean localFileHasErrorForPutOperation (File file ) {
303+ if (!file .exists () || file .isDirectory ()) {
304+ LoggingUtil .log (
305+ LogLevel .ERROR ,
306+ String .format ("Local file does not exist or is a directory {%s}" , localFilePath ));
307+ status = VolumeOperationStatus .ABORTED ;
308+ errorMessage = "Local file does not exist or is a directory" ;
309+ return true ;
310+ }
311+ if (file .length () == 0 ) {
312+ LoggingUtil .log (LogLevel .ERROR , String .format ("Local file is empty {%s}" , localFilePath ));
313+ status = VolumeOperationStatus .ABORTED ;
314+ errorMessage = "Local file is empty" ;
315+ return true ;
316+ }
317+
318+ if (file .length () > PUT_SIZE_LIMITS ) {
319+ LoggingUtil .log (LogLevel .ERROR , String .format ("Local file too large {%s}" , localFilePath ));
320+ status = VolumeOperationStatus .ABORTED ;
321+ errorMessage = "Local file too large" ;
322+ return true ;
323+ }
324+ return false ;
325+ }
326+
257327 private void executeDeleteOperation () {
258328 // TODO: Check for AWS specific handling
259329 HttpDelete httpDelete = new HttpDelete (operationUrl );
0 commit comments