This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new a90fd026dd Add "SQL from file" functionality to "Table Input" 
transform, fixes #5182 (#6601)
a90fd026dd is described below

commit a90fd026dd17259e17348147b60e154e53597d1e
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Feb 18 08:22:47 2026 +0100

    Add "SQL from file" functionality to "Table Input" transform, fixes #5182 
(#6601)
---
 .../database/0008-table-input-with-sql-file.hpl    | 213 +++++++++++++++++++++
 .../database/main-0008-table-input.hwf             | 142 ++++++++------
 integration-tests/database/scripts/testquery.sql   |  18 ++
 .../pipeline/transforms/tableinput/TableInput.java |  16 +-
 .../transforms/tableinput/TableInputDialog.java    |  90 ++++++++-
 .../transforms/tableinput/TableInputMeta.java      |  88 +++++++--
 .../tableinput/messages/messages_en_US.properties  |   6 +
 7 files changed, 492 insertions(+), 81 deletions(-)

diff --git a/integration-tests/database/0008-table-input-with-sql-file.hpl 
b/integration-tests/database/0008-table-input-with-sql-file.hpl
new file mode 100644
index 0000000000..31d96739a2
--- /dev/null
+++ b/integration-tests/database/0008-table-input-with-sql-file.hpl
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<pipeline>
+  <info>
+    <name>0008-table-input-with-sql-file</name>
+    <name_sync_with_filename>Y</name_sync_with_filename>
+    <description/>
+    <extended_description/>
+    <pipeline_version/>
+    <pipeline_type>Normal</pipeline_type>
+    <pipeline_status>0</pipeline_status>
+    <parameters>
+    </parameters>
+    <capture_transform_performance>N</capture_transform_performance>
+    
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
+    
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+    <created_user>-</created_user>
+    <created_date>2021/04/29 15:06:59.515</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2021/04/29 15:06:59.515</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>Table input</from>
+      <to>count rows</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>count rows</from>
+      <to>check count</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>check count</from>
+      <to>success</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>check count</from>
+      <to>Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>432</xloc>
+      <yloc>176</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Table input</name>
+    <type>TableInput</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <connection>unit-test-db</connection>
+    <execute_each_row>N</execute_each_row>
+    <limit>0</limit>
+    <sql>/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+select * from public.testtable</sql>
+    <sql_from_file>${PROJECT_HOME}/scripts/testquery.sql</sql_from_file>
+    <variables_active>N</variables_active>
+    <attributes/>
+    <GUI>
+      <xloc>144</xloc>
+      <yloc>80</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>check count</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <compare>
+      <condition>
+        <conditions>
+</conditions>
+        <function>=</function>
+        <leftvalue>value</leftvalue>
+        <negated>N</negated>
+        <operator>-</operator>
+        <value>
+          <isnull>N</isnull>
+          <length>-1</length>
+          <mask>####0;-####0</mask>
+          <name>constant</name>
+          <precision>0</precision>
+          <text>5</text>
+          <type>Integer</type>
+        </value>
+      </condition>
+    </compare>
+    <send_false_to>Abort</send_false_to>
+    <send_true_to>success</send_true_to>
+    <attributes/>
+    <GUI>
+      <xloc>432</xloc>
+      <yloc>80</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>count rows</name>
+    <type>GroupBy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <add_linenr>N</add_linenr>
+    <all_rows>N</all_rows>
+    <directory>${java.io.tmpdir}</directory>
+    <fields>
+      <field>
+        <aggregate>value</aggregate>
+        <subject>value</subject>
+        <type>COUNT_ANY</type>
+      </field>
+    </fields>
+    <give_back_row>N</give_back_row>
+    <group>
+</group>
+    <ignore_aggregate>N</ignore_aggregate>
+    <prefix>grp</prefix>
+    <attributes/>
+    <GUI>
+      <xloc>304</xloc>
+      <yloc>80</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>success</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>576</xloc>
+      <yloc>80</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/integration-tests/database/main-0008-table-input.hwf 
b/integration-tests/database/main-0008-table-input.hwf
index 740d350024..1e4580a1fb 100644
--- a/integration-tests/database/main-0008-table-input.hwf
+++ b/integration-tests/database/main-0008-table-input.hwf
@@ -35,14 +35,15 @@ limitations under the License.
       <description/>
       <type>SPECIAL</type>
       <attributes/>
-      <repeat>N</repeat>
-      <schedulerType>0</schedulerType>
-      <intervalSeconds>0</intervalSeconds>
-      <intervalMinutes>60</intervalMinutes>
+      <DayOfMonth>1</DayOfMonth>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
       <hour>12</hour>
+      <intervalMinutes>60</intervalMinutes>
+      <intervalSeconds>0</intervalSeconds>
       <minutes>0</minutes>
+      <repeat>N</repeat>
+      <schedulerType>0</schedulerType>
       <weekDay>1</weekDay>
-      <DayOfMonth>1</DayOfMonth>
       <parallel>N</parallel>
       <xloc>64</xloc>
       <yloc>48</yloc>
@@ -53,25 +54,22 @@ limitations under the License.
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <filename>${PROJECT_HOME}/0008-table-input.hpl</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <clear_rows>N</clear_rows>
-      <clear_files>N</clear_files>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
       <add_date>N</add_date>
       <add_time>N</add_time>
-      <loglevel>Basic</loglevel>
-      <set_append_logfile>N</set_append_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
       <create_parent_folder>N</create_parent_folder>
-      <run_configuration>local</run_configuration>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/0008-table-input.hpl</filename>
+      <loglevel>Basic</loglevel>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
       <xloc>400</xloc>
       <yloc>48</yloc>
@@ -82,27 +80,24 @@ limitations under the License.
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <filename>${PROJECT_HOME}/0008-table-input-with-parameter.hpl</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <clear_rows>N</clear_rows>
-      <clear_files>N</clear_files>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
       <add_date>N</add_date>
       <add_time>N</add_time>
-      <loglevel>Basic</loglevel>
-      <set_append_logfile>N</set_append_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
       <create_parent_folder>N</create_parent_folder>
-      <run_configuration>local</run_configuration>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/0008-table-input-with-parameter.hpl</filename>
+      <loglevel>Basic</loglevel>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
-      <xloc>608</xloc>
+      <xloc>880</xloc>
       <yloc>48</yloc>
       <attributes_hac/>
     </action>
@@ -111,27 +106,24 @@ limitations under the License.
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <filename>${PROJECT_HOME}/0008-table-input-with-variable.hpl</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <clear_rows>N</clear_rows>
-      <clear_files>N</clear_files>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
       <add_date>N</add_date>
       <add_time>N</add_time>
-      <loglevel>Basic</loglevel>
-      <set_append_logfile>N</set_append_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
       <create_parent_folder>N</create_parent_folder>
-      <run_configuration>local</run_configuration>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/0008-table-input-with-variable.hpl</filename>
+      <loglevel>Basic</loglevel>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
-      <xloc>864</xloc>
+      <xloc>1136</xloc>
       <yloc>48</yloc>
       <attributes_hac/>
     </action>
@@ -140,26 +132,46 @@ limitations under the License.
       <description/>
       <type>SQL</type>
       <attributes/>
-      <sql/>
-      <useVariableSubstitution>F</useVariableSubstitution>
-      <sqlfromfile>T</sqlfromfile>
-      <sqlfilename>${PROJECT_HOME}/scripts/script2.sql</sqlfilename>
-      <sendOneStatement>F</sendOneStatement>
       <connection>unit-test-db</connection>
+      <sendOneStatement>N</sendOneStatement>
+      <sqlfilename>${PROJECT_HOME}/scripts/script2.sql</sqlfilename>
+      <sqlfromfile>Y</sqlfromfile>
+      <useVariableSubstitution>N</useVariableSubstitution>
       <parallel>N</parallel>
       <xloc>208</xloc>
       <yloc>48</yloc>
       <attributes_hac/>
     </action>
+    <action>
+      <name>0008-table-input-with-sql-file.hpl</name>
+      <description/>
+      <type>PIPELINE</type>
+      <attributes/>
+      <add_date>N</add_date>
+      <add_time>N</add_time>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
+      <create_parent_folder>N</create_parent_folder>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/0008-table-input-with-sql-file.hpl</filename>
+      <logext/>
+      <logfile/>
+      <loglevel>Basic</loglevel>
+      <parameters>
+        <pass_all_parameters>Y</pass_all_parameters>
+      </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
+      <parallel>N</parallel>
+      <xloc>608</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
   </actions>
   <hops>
-    <hop>
-      <from>0008-table-input.hpl</from>
-      <to>0008-table-input-with-parameter.hpl</to>
-      <enabled>Y</enabled>
-      <evaluation>Y</evaluation>
-      <unconditional>N</unconditional>
-    </hop>
     <hop>
       <from>0008-table-input-with-parameter.hpl</from>
       <to>0008-table-input-with-variable.hpl</to>
@@ -181,6 +193,20 @@ limitations under the License.
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
     </hop>
+    <hop>
+      <from>0008-table-input.hpl</from>
+      <to>0008-table-input-with-sql-file.hpl</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>0008-table-input-with-sql-file.hpl</from>
+      <to>0008-table-input-with-parameter.hpl</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
   </hops>
   <notepads>
   </notepads>
diff --git a/integration-tests/database/scripts/testquery.sql 
b/integration-tests/database/scripts/testquery.sql
new file mode 100644
index 0000000000..c5290dd285
--- /dev/null
+++ b/integration-tests/database/scripts/testquery.sql
@@ -0,0 +1,18 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+select * from public.testtable
\ No newline at end of file
diff --git 
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
 
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
index d986deda35..9a18cc44ce 100644
--- 
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
+++ 
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInput.java
@@ -217,11 +217,17 @@ public class TableInput extends 
BaseTransform<TableInputMeta, TableInputData> {
     boolean success = true;
 
     // Open the query with the optional parameters received from the source 
transforms.
-    String sql = null;
+    String sql;
+    try {
+      sql = meta.getEffectiveSql(variables);
+    } catch (HopException e) {
+      logError("Could not get SQL: " + e.getMessage());
+      setErrors(1);
+      stopAll();
+      return false;
+    }
     if (meta.isVariableReplacementActive()) {
-      sql = resolve(meta.getSql());
-    } else {
-      sql = meta.getSql();
+      sql = resolve(sql);
     }
 
     if (isDetailed()) {
@@ -304,7 +310,7 @@ public class TableInput extends 
BaseTransform<TableInputMeta, TableInputData> {
       // Verify some basic things first...
       //
       boolean passed = true;
-      if (Utils.isEmpty(meta.getSql())) {
+      if (Utils.isEmpty(meta.getSql()) && 
Utils.isEmpty(meta.getSqlFromFile())) {
         logError(BaseMessages.getString(PKG, 
"TableInput.Exception.SQLIsNeeded"));
         passed = false;
       }
diff --git 
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
 
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
index 0d255931a6..961cc813ce 100644
--- 
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
+++ 
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java
@@ -25,10 +25,12 @@ import org.apache.hop.core.Props;
 import org.apache.hop.core.database.Database;
 import org.apache.hop.core.database.DatabaseMeta;
 import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.exception.HopFileException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.core.vfs.HopVfs;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.pipeline.Pipeline;
 import org.apache.hop.pipeline.PipelineMeta;
@@ -85,6 +87,10 @@ public class TableInputDialog extends BaseTransformDialog {
 
   private Label wlPosition;
 
+  private Label wlSqlFromFile;
+  private TextVar wSqlFromFile;
+  private Button wbSqlFromFile;
+
   public TableInputDialog(
       Shell parent, IVariables variables, TableInputMeta transformMeta, 
PipelineMeta pipelineMeta) {
     super(parent, variables, transformMeta, pipelineMeta);
@@ -101,6 +107,56 @@ public class TableInputDialog extends BaseTransformDialog {
     wConnection = addConnectionLine(shell, wSpacer, input.getConnection(), 
lsMod);
     wConnection.addListener(SWT.Selection, e -> getSqlReservedWords());
 
+    // Load SQL from file
+    wlSqlFromFile = new Label(shell, SWT.RIGHT);
+    wlSqlFromFile.setText(BaseMessages.getString(PKG, 
"TableInputDialog.LoadSqlFromFile"));
+    PropsUi.setLook(wlSqlFromFile);
+    FormData fdlSqlFromFile = new FormData();
+    fdlSqlFromFile.left = new FormAttachment(0, 0);
+    fdlSqlFromFile.right = new FormAttachment(middle, -margin);
+    fdlSqlFromFile.top = new FormAttachment(wConnection, margin);
+    wlSqlFromFile.setLayoutData(fdlSqlFromFile);
+    wbSqlFromFile = new Button(shell, SWT.PUSH);
+    PropsUi.setLook(wbSqlFromFile);
+    wbSqlFromFile.setText(BaseMessages.getString(PKG, 
"TableInputDialog.Browse"));
+    FormData fdbSqlFromFile = new FormData();
+    fdbSqlFromFile.right = new FormAttachment(100, 0);
+    fdbSqlFromFile.top = new FormAttachment(wlSqlFromFile, 0, SWT.CENTER);
+    wbSqlFromFile.setLayoutData(fdbSqlFromFile);
+
+    wSqlFromFile = new TextVar(variables, shell, SWT.SINGLE | SWT.LEFT | 
SWT.BORDER);
+    PropsUi.setLook(wSqlFromFile);
+    wSqlFromFile.addModifyListener(lsMod);
+    FormData fdSqlFromFile = new FormData();
+    fdSqlFromFile.left = new FormAttachment(middle, 0);
+    fdSqlFromFile.right = new FormAttachment(wbSqlFromFile, -margin);
+    fdSqlFromFile.top = new FormAttachment(wlSqlFromFile, 0, SWT.CENTER);
+    wSqlFromFile.setLayoutData(fdSqlFromFile);
+    wbSqlFromFile.addListener(
+        SWT.Selection,
+        e -> {
+          String path =
+              BaseDialog.presentFileDialog(
+                  shell,
+                  wSqlFromFile,
+                  variables,
+                  new String[] {"*.sql", "*"},
+                  new String[] {
+                    BaseMessages.getString(PKG, "TableInputDialog.SqlFiles"),
+                    BaseMessages.getString(PKG, "System.FileType.AllFiles")
+                  },
+                  false);
+          if (path != null) {
+            loadSqlFromFileAndSetReadOnly();
+          }
+        });
+    wSqlFromFile.addModifyListener(
+        e -> {
+          if (Utils.isEmpty(wSqlFromFile.getText())) {
+            wSql.setEditable(true);
+          }
+        });
+
     // Limit input ...
     Label wlLimit = new Label(shell, SWT.RIGHT);
     wlLimit.setText(BaseMessages.getString(PKG, "TableInputDialog.LimitSize"));
@@ -209,7 +265,7 @@ public class TableInputDialog extends BaseTransformDialog {
     PropsUi.setLook(wlSql);
     FormData fdlSql = new FormData();
     fdlSql.left = new FormAttachment(0, 0);
-    fdlSql.top = new FormAttachment(wConnection, margin);
+    fdlSql.top = new FormAttachment(wbSqlFromFile, margin);
     wlSql.setLayoutData(fdlSql);
 
     Button wbTable = new Button(shell, SWT.PUSH | SWT.CENTER);
@@ -217,7 +273,7 @@ public class TableInputDialog extends BaseTransformDialog {
     wbTable.setText(BaseMessages.getString(PKG, 
"TableInputDialog.GetSQLAndSelectStatement"));
     FormData fdbTable = new FormData();
     fdbTable.right = new FormAttachment(100, 0);
-    fdbTable.top = new FormAttachment(wConnection, margin);
+    fdbTable.top = new FormAttachment(wbSqlFromFile, margin);
     wbTable.setLayoutData(fdbTable);
 
     if (EnvironmentUtils.getInstance().isWeb()) {
@@ -330,6 +386,28 @@ public class TableInputDialog extends BaseTransformDialog {
             PKG, "TableInputDialog.Position.Label", "" + lineNumber, "" + 
columnNumber));
   }
 
+  private void loadSqlFromFileAndSetReadOnly() {
+    String path = variables.resolve(wSqlFromFile.getText());
+    if (Utils.isEmpty(path)) {
+      wSql.setEditable(true);
+      return;
+    }
+    try {
+      String content = HopVfs.getTextFileContent(path, Const.XML_ENCODING);
+      wSql.setText(content);
+      wSql.setEditable(false);
+    } catch (HopFileException e) {
+      MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_WARNING);
+      mb.setText(BaseMessages.getString(PKG, 
"TableInputDialog.DialogCaptionError"));
+      mb.setMessage(
+          BaseMessages.getString(PKG, 
"TableInputDialog.CouldNotLoadSqlFromFile", path)
+              + Const.CR
+              + e.getMessage());
+      mb.open();
+      wSql.setEditable(true);
+    }
+  }
+
   protected void setSqlToolTip() {
     if (wVariables.getSelection()) {
       wSql.setToolTipText(variables.resolve(wSql.getText()));
@@ -346,6 +424,13 @@ public class TableInputDialog extends BaseTransformDialog {
       wConnection.setText(input.getConnection());
     }
 
+    wSqlFromFile.setText(Const.NVL(input.getSqlFromFile(), ""));
+    if (!Utils.isEmpty(wSqlFromFile.getText())) {
+      loadSqlFromFileAndSetReadOnly();
+    } else {
+      wSql.setEditable(true);
+    }
+
     wLimit.setText(Const.NVL(input.getRowLimit(), ""));
     wDataFrom.setText(Const.NVL(input.getLookup(), ""));
     wEachRow.setSelection(input.isExecuteEachInputRow());
@@ -369,6 +454,7 @@ public class TableInputDialog extends BaseTransformDialog {
         preview && !Utils.isEmpty(wSql.getSelectionText())
             ? wSql.getSelectionText()
             : wSql.getText());
+    meta.setSqlFromFile(wSqlFromFile.getText());
 
     meta.setRowLimit(wLimit.getText());
     meta.setExecuteEachInputRow(wEachRow.getSelection());
diff --git 
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java
 
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java
index a245ecfd72..7eeb2aa14b 100644
--- 
a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java
+++ 
b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java
@@ -27,6 +27,7 @@ import org.apache.hop.core.database.Database;
 import org.apache.hop.core.database.DatabaseMeta;
 import org.apache.hop.core.exception.HopDatabaseException;
 import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.exception.HopFileException;
 import org.apache.hop.core.exception.HopTransformException;
 import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.row.IRowMeta;
@@ -35,6 +36,7 @@ import org.apache.hop.core.row.RowDataUtil;
 import org.apache.hop.core.row.RowMeta;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.core.vfs.HopVfs;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.HopMetadataPropertyType;
@@ -88,6 +90,12 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
 
   @HopMetadataProperty private String lookup;
 
+  /**
+   * When set, SQL is loaded from this file (VFS path, supports variables). 
SQL editor is read-only.
+   */
+  @HopMetadataProperty(key = "sql_from_file", injectionKey = "SQL_FROM_FILE")
+  private String sqlFromFile;
+
   public TableInputMeta() {
     super();
   }
@@ -150,6 +158,32 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
     this.lookup = lookup;
   }
 
+  public String getSqlFromFile() {
+    return sqlFromFile;
+  }
+
+  public void setSqlFromFile(String sqlFromFile) {
+    this.sqlFromFile = sqlFromFile;
+  }
+
+  /**
+   * Returns the SQL to execute: either from the inline editor or loaded from 
the file specified by
+   * sqlFromFile (using VFS). Variables are resolved in the file path.
+   */
+  public String getEffectiveSql(IVariables variables) throws HopException {
+    if (!Utils.isEmpty(sqlFromFile)) {
+      String path = variables.resolve(sqlFromFile);
+      try {
+        return HopVfs.getTextFileContent(path, Const.XML_ENCODING);
+      } catch (HopFileException e) {
+        throw new HopException(
+            BaseMessages.getString(PKG, 
"TableInputMeta.Exception.CouldNotLoadSqlFromFile", path),
+            e);
+      }
+    }
+    return sql;
+  }
+
   @Override
   public Object clone() {
     TableInputMeta retval = (TableInputMeta) super.clone();
@@ -189,9 +223,15 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
     super.databases = new Database[] {db}; // keep track of it for canceling 
purposes...
 
     // First try without connecting to the database... (can be S L O W)
-    String sNewSql = sql;
+    String effectiveSql;
+    try {
+      effectiveSql = getEffectiveSql(variables);
+    } catch (HopException e) {
+      throw new HopTransformException(e.getMessage(), e);
+    }
+    String sNewSql = effectiveSql;
     if (isVariableReplacementActive()) {
-      sNewSql = db.resolve(sql);
+      sNewSql = db.resolve(effectiveSql);
       if (variables != null) {
         sNewSql = variables.resolve(sNewSql);
       }
@@ -277,6 +317,18 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
       IHopMetadataProvider metadataProvider) {
     CheckResult cr;
 
+    String effectiveSql = null;
+    try {
+      effectiveSql = getEffectiveSql(variables);
+    } catch (HopException e) {
+      cr =
+          new CheckResult(
+              ICheckResult.TYPE_RESULT_ERROR,
+              "Could not get SQL: " + e.getMessage(),
+              transformMeta);
+      remarks.add(cr);
+    }
+
     DatabaseMeta databaseMeta = null;
 
     try {
@@ -308,16 +360,18 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
                 ICheckResult.TYPE_RESULT_OK, "Connection to database OK", 
transformMeta);
         remarks.add(cr);
 
-        if (!Utils.isEmpty(sql)) {
-          cr =
-              new CheckResult(
-                  ICheckResult.TYPE_RESULT_OK, "SQL statement is entered", 
transformMeta);
-          remarks.add(cr);
-        } else {
-          cr =
-              new CheckResult(
-                  ICheckResult.TYPE_RESULT_ERROR, "SQL statement is missing.", 
transformMeta);
-          remarks.add(cr);
+        if (effectiveSql != null) {
+          if (!Utils.isEmpty(effectiveSql)) {
+            cr =
+                new CheckResult(
+                    ICheckResult.TYPE_RESULT_OK, "SQL statement is entered", 
transformMeta);
+            remarks.add(cr);
+          } else {
+            cr =
+                new CheckResult(
+                    ICheckResult.TYPE_RESULT_ERROR, "SQL statement is 
missing.", transformMeta);
+            remarks.add(cr);
+          }
         }
       } catch (HopException e) {
         cr =
@@ -369,12 +423,13 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
 
       // Count the number of ? in the SQL string:
       int count = 0;
-      for (int i = 0; i < sql.length(); i++) {
-        char c = sql.charAt(i);
+      String sqlForParams = (effectiveSql != null) ? effectiveSql : "";
+      for (int i = 0; i < sqlForParams.length(); i++) {
+        char c = sqlForParams.charAt(i);
         if (c == '\'') { // skip to next quote!
           do {
             i++;
-            c = sql.charAt(i);
+            c = sqlForParams.charAt(i);
           } while (c != '\'');
         }
         if (c == '?') {
@@ -458,6 +513,7 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
     try {
       DatabaseMeta databaseMeta =
           
metadataProvider.getSerializer(DatabaseMeta.class).load(variables.resolve(connection));
+      String effectiveSql = getEffectiveSql(variables);
 
       // Find the lookupfields...
       IRowMeta out = new RowMeta();
@@ -478,7 +534,7 @@ public class TableInputMeta extends 
BaseTransformMeta<TableInput, TableInputData
                   outvalue.getName(),
                   outvalue.getName(),
                   transformMeta.getName(),
-                  sql,
+                  effectiveSql,
                   "read from one or more database tables via SQL statement");
           impact.add(ii);
         }
diff --git 
a/plugins/transforms/tableinput/src/main/resources/org/apache/hop/pipeline/transforms/tableinput/messages/messages_en_US.properties
 
b/plugins/transforms/tableinput/src/main/resources/org/apache/hop/pipeline/transforms/tableinput/messages/messages_en_US.properties
index 291103399c..f199d7e4d1 100644
--- 
a/plugins/transforms/tableinput/src/main/resources/org/apache/hop/pipeline/transforms/tableinput/messages/messages_en_US.properties
+++ 
b/plugins/transforms/tableinput/src/main/resources/org/apache/hop/pipeline/transforms/tableinput/messages/messages_en_US.properties
@@ -37,9 +37,13 @@ TableInputDialog.LimitSize=Limit size
 TableInputDialog.NumberOfRowsToPreview=Enter the number of rows you would like 
to preview\:
 TableInputDialog.PerhapsNoPermissions=Perhaps you don''t have the right 
permissions?
 TableInputDialog.Position.Label=Line {0} Column {1}
+TableInputDialog.Browse=Browse...
+TableInputDialog.CouldNotLoadSqlFromFile=Could not load SQL from file: {0}
+TableInputDialog.LoadSqlFromFile=Load SQL from file
 TableInputDialog.ReplaceVariables=Replace variables in script
 TableInputDialog.SelectValidConnection=Please select a valid connection\!
 TableInputDialog.SQL=SQL
+TableInputDialog.SqlFiles=SQL files
 TableInputDialog.TableInput=Table input
 TableInputDialog.TransformName=Transform name
 TableInputMeta.CheckResult.DatabaseMetaError=Unable to get a reference to 
databaseMeta for connection: ''{0}''
@@ -50,4 +54,6 @@ TableInputMeta.Injection.LAZY_CONVERSION=Enable this option 
to optimize data typ
 TableInputMeta.Injection.LIMIT=The maximum number of lines to read.
 TableInputMeta.Injection.REPLACE_VARIABLES=Enable this option to replace 
variables in the script.
 TableInputMeta.Injection.SQL=The SQL statement used to read information from 
the database connection.
+TableInputMeta.Exception.CouldNotLoadSqlFromFile=Could not load SQL from file: 
{0}
 TableInputMeta.keyword=table,input
+System.FileType.AllFiles=All files

Reply via email to