Skip to content

Commit c296e9d

Browse files
authored
Add splitting of long SQL queries in DBQLSqlTbl (#312)
The `SqlTextInfo` column of the `dbc.DBQLSqlTbl` table may need splitting due to 64kB row size limit on Teradata 15. This change introduces a new command-line option that allows for splitting this column into multiple rows if the length is longer than the specified max length. By default, if the option is not used the column is extracted as is, which may reach 31000 characters. With the option, the column is split into multiple rows. The max length can be any integer between 5000 and 30000 characters. Example usage: ``` -Dteradata-logs.max-sql-length=7000 ```
1 parent bc5a321 commit c296e9d

File tree

6 files changed

+175
-27
lines changed

6 files changed

+175
-27
lines changed

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/teradata/TeradataAssessmentLogsJdbcTask.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.edwmigration.dumper.application.dumper.connector.ZonedInterval;
2222
import com.google.edwmigration.dumper.application.dumper.connector.teradata.AbstractTeradataConnector.SharedState;
2323
import java.util.List;
24+
import java.util.OptionalLong;
2425
import java.util.function.Predicate;
2526
import javax.annotation.CheckForNull;
2627
import javax.annotation.Nonnull;
@@ -162,8 +163,18 @@ public TeradataAssessmentLogsJdbcTask(
162163
List<String> conditions,
163164
ZonedInterval interval,
164165
@CheckForNull String logDateColumn,
166+
OptionalLong maxSqlLength,
165167
List<String> orderBy) {
166-
super(targetPath, state, logTable, queryTable, conditions, interval, logDateColumn, orderBy);
168+
super(
169+
targetPath,
170+
state,
171+
logTable,
172+
queryTable,
173+
conditions,
174+
interval,
175+
logDateColumn,
176+
maxSqlLength,
177+
orderBy);
167178
}
168179

169180
@Nonnull

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/teradata/TeradataLogsConnector.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.auto.service.AutoService;
2222
import com.google.common.collect.ImmutableList;
2323
import com.google.common.collect.ImmutableMap;
24+
import com.google.common.collect.Range;
2425
import com.google.edwmigration.dumper.application.dumper.ConnectorArguments;
2526
import com.google.edwmigration.dumper.application.dumper.MetadataDumperUsageException;
2627
import com.google.edwmigration.dumper.application.dumper.annotations.RespectsArgumentAssessment;
@@ -38,13 +39,15 @@
3839
import com.google.edwmigration.dumper.application.dumper.task.FormatTask;
3940
import com.google.edwmigration.dumper.application.dumper.task.Task;
4041
import com.google.edwmigration.dumper.application.dumper.task.TaskCategory;
42+
import com.google.edwmigration.dumper.application.dumper.utils.PropertyParser;
4143
import com.google.edwmigration.dumper.plugin.ext.jdk.annotation.Description;
4244
import com.google.edwmigration.dumper.plugin.lib.dumper.spi.TeradataLogsDumpFormat;
4345
import java.time.Duration;
4446
import java.time.format.DateTimeFormatter;
4547
import java.util.ArrayList;
4648
import java.util.Arrays;
4749
import java.util.List;
50+
import java.util.OptionalLong;
4851
import javax.annotation.Nonnull;
4952
import org.apache.commons.lang3.StringUtils;
5053
import org.slf4j.Logger;
@@ -62,6 +65,10 @@ public class TeradataLogsConnector extends AbstractTeradataConnector
6265

6366
private static final Logger LOG = LoggerFactory.getLogger(TeradataLogsConnector.class);
6467
private static final String ASSESSMENT_DEF_LOG_TABLE = "dbc.QryLogV";
68+
static final int DBQLSQLTBL_SQLTEXTINFO_LENGTH = 31000;
69+
70+
private static final Range<Long> MAX_SQL_LENGTH_RANGE =
71+
Range.closed(5000L, (long) DBQLSQLTBL_SQLTEXTINFO_LENGTH);
6572

6673
public enum TeradataLogsConnectorProperty implements ConnectorProperty {
6774
UTILITY_LOGS_TABLE(
@@ -81,6 +88,12 @@ public enum TeradataLogsConnectorProperty implements ConnectorProperty {
8188
"The name of the column of type DATE to include in the WHERE clause when dumping"
8289
+ " query log tables. The column must exist in both tables."
8390
+ " See --query-log-alternates for query log table names.",
91+
/* defaultValue= */ null),
92+
MAX_SQL_LENGTH(
93+
"max-sql-length",
94+
"Max length of the DBQLSqlTbl.SqlTextInfo column."
95+
+ " Text that is longer than the defined limit will be split into multiple rows."
96+
+ " Example: 10000. Allowed range: 5000-30000.",
8497
/* defaultValue= */ null);
8598

8699
private final String name;
@@ -174,9 +187,6 @@ public void addTasksTo(List<? super Task<?>> out, @Nonnull ConnectorArguments ar
174187
conditions.add("L.StartTime >= " + arguments.getQueryLogEarliestTimestamp());
175188
}
176189

177-
// Beware of Teradata SQLSTATE HY000. See issue #4126.
178-
// Most likely caused by some operation (equality?) being performed on a datum which is too long
179-
// for a varchar.
180190
Duration rotationDuration = arguments.getQueryLogRotationFrequency();
181191
ZonedIntervalIterable intervals =
182192
ZonedIntervalIterableGenerator.forConnectorArguments(
@@ -187,6 +197,9 @@ public void addTasksTo(List<? super Task<?>> out, @Nonnull ConnectorArguments ar
187197
String utilityLogsTable =
188198
arguments.getDefinitionOrDefault(TeradataLogsConnectorProperty.UTILITY_LOGS_TABLE);
189199
String logDateColumn = arguments.getDefinition(TeradataLogsConnectorProperty.LOG_DATE_COLUMN);
200+
OptionalLong maxSqlLength =
201+
PropertyParser.parseNumber(
202+
arguments, TeradataLogsConnectorProperty.MAX_SQL_LENGTH, MAX_SQL_LENGTH_RANGE);
190203
for (ZonedInterval interval : intervals) {
191204
String file = createFilename(ZIP_ENTRY_PREFIX, interval);
192205
if (isAssessment) {
@@ -200,6 +213,7 @@ public void addTasksTo(List<? super Task<?>> out, @Nonnull ConnectorArguments ar
200213
conditions,
201214
interval,
202215
logDateColumn,
216+
maxSqlLength,
203217
orderBy)
204218
.withHeaderClass(HeaderForAssessment.class));
205219
out.addAll(createTimeSeriesTasks(interval, arguments));

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/teradata/TeradataLogsJdbcTask.java

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@
1616
*/
1717
package com.google.edwmigration.dumper.application.dumper.connector.teradata;
1818

19+
import static com.google.edwmigration.dumper.application.dumper.connector.teradata.TeradataLogsConnector.DBQLSQLTBL_SQLTEXTINFO_LENGTH;
1920
import static com.google.edwmigration.dumper.application.dumper.connector.teradata.TeradataUtils.formatQuery;
21+
import static com.google.edwmigration.dumper.application.dumper.connector.teradata.TeradataUtils.optionalIf;
2022

2123
import com.google.common.annotations.VisibleForTesting;
2224
import com.google.common.base.Joiner;
2325
import com.google.common.base.Preconditions;
2426
import com.google.common.base.Predicates;
27+
import com.google.common.collect.ImmutableList;
2528
import com.google.common.io.ByteSink;
29+
import com.google.common.primitives.Ints;
2630
import com.google.edwmigration.dumper.application.dumper.connector.ZonedInterval;
2731
import com.google.edwmigration.dumper.application.dumper.connector.teradata.AbstractTeradataConnector.SharedState;
2832
import com.google.edwmigration.dumper.application.dumper.handle.JdbcHandle;
@@ -36,6 +40,7 @@
3640
import java.time.format.DateTimeFormatter;
3741
import java.util.Collections;
3842
import java.util.List;
43+
import java.util.OptionalLong;
3944
import java.util.function.Function;
4045
import java.util.function.Predicate;
4146
import javax.annotation.CheckForNull;
@@ -93,6 +98,7 @@ public class TeradataLogsJdbcTask extends AbstractJdbcTask<Summary> {
9398
protected final List<String> conditions;
9499
protected final ZonedInterval interval;
95100
@CheckForNull private final String logDateColumn;
101+
private final OptionalLong maxSqlLength;
96102
protected final List<String> orderBy;
97103

98104
public TeradataLogsJdbcTask(
@@ -110,6 +116,7 @@ public TeradataLogsJdbcTask(
110116
conditions,
111117
interval,
112118
/* logDateColumn= */ null,
119+
/* maxSqlLength= */ OptionalLong.empty(),
113120
Collections.emptyList());
114121
}
115122

@@ -121,6 +128,7 @@ protected TeradataLogsJdbcTask(
121128
List<String> conditions,
122129
ZonedInterval interval,
123130
@CheckForNull String logDateColumn,
131+
OptionalLong maxSqlLength,
124132
List<String> orderBy) {
125133
super(targetPath);
126134
this.state = Preconditions.checkNotNull(state, "SharedState was null.");
@@ -129,6 +137,7 @@ protected TeradataLogsJdbcTask(
129137
this.conditions = conditions;
130138
this.interval = interval;
131139
this.logDateColumn = logDateColumn;
140+
this.maxSqlLength = maxSqlLength;
132141
this.orderBy = orderBy;
133142
}
134143

@@ -190,7 +199,16 @@ private String getSql(@Nonnull JdbcHandle handle) {
190199
// "QueryID is a system-wide unique field; you can use QueryID
191200
// to join DBQL tables ... without needing ProcID as an additional join field."
192201
// (https://docs.teradata.com/reader/B7Lgdw6r3719WUyiCSJcgw/YIKoBz~QQgv2Aw5dF339kA)
193-
buf.append(" LEFT OUTER JOIN ").append(queryTable).append(" ST ON (L.QueryID=ST.QueryID");
202+
buf.append(" LEFT OUTER JOIN ");
203+
if (maxSqlLength.isPresent()) {
204+
buf.append('(')
205+
.append(
206+
createSubQueryWithSplittingLongQueries(Ints.checkedCast(maxSqlLength.getAsLong())))
207+
.append(')');
208+
} else {
209+
buf.append(queryTable);
210+
}
211+
buf.append(" ST ON (L.QueryID=ST.QueryID");
194212

195213
if (logDateColumn != null) {
196214
buf.append(" AND L.").append(logDateColumn).append("=ST.").append(logDateColumn);
@@ -214,12 +232,7 @@ private String getSql(@Nonnull JdbcHandle handle) {
214232
SQL_FORMAT.format(interval.getStart()), SQL_FORMAT.format(interval.getEndExclusive())));
215233

216234
if (logDateColumn != null) {
217-
buf.append(" AND L.")
218-
.append(logDateColumn)
219-
.append(" = ")
220-
.append("CAST('")
221-
.append(SQL_DATE_FORMAT.format(interval.getStart()))
222-
.append("' AS DATE)");
235+
buf.append(" AND L.").append(createLogDateColumnCondition());
223236
}
224237

225238
for (String condition : conditions) {
@@ -233,6 +246,27 @@ private String getSql(@Nonnull JdbcHandle handle) {
233246
return buf.toString().replace('\n', ' ');
234247
}
235248

249+
private String createLogDateColumnCondition() {
250+
return logDateColumn + " = CAST('" + SQL_DATE_FORMAT.format(interval.getStart()) + "' AS DATE)";
251+
}
252+
253+
private String createSubQueryWithSplittingLongQueries(int maxLength) {
254+
ImmutableList.Builder<String> columns = ImmutableList.<String>builder().add("QueryID");
255+
if (logDateColumn != null) {
256+
columns.add(logDateColumn);
257+
}
258+
return new SplitTextColumnQueryGenerator(
259+
columns.build(),
260+
"SqlTextInfo",
261+
"SqlRowNo",
262+
queryTable,
263+
/* whereCondition=*/ optionalIf(
264+
logDateColumn != null, this::createLogDateColumnCondition),
265+
DBQLSQLTBL_SQLTEXTINFO_LENGTH,
266+
maxLength)
267+
.generate();
268+
}
269+
236270
/**
237271
* Runs a test query to check whether a given projection expression is legal on this Teradata
238272
* instance.

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/teradata/TeradataMetadataConnector.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.edwmigration.dumper.application.dumper.connector.teradata;
1818

1919
import static com.google.edwmigration.dumper.application.dumper.connector.teradata.TeradataUtils.formatQuery;
20+
import static com.google.edwmigration.dumper.application.dumper.connector.teradata.TeradataUtils.optionalIf;
2021
import static java.util.stream.Collectors.joining;
2122
import static java.util.stream.Collectors.toList;
2223

@@ -42,7 +43,6 @@
4243
import java.util.List;
4344
import java.util.Optional;
4445
import java.util.OptionalLong;
45-
import java.util.function.Supplier;
4646
import javax.annotation.Nonnull;
4747

4848
/** @author miguel */
@@ -70,7 +70,7 @@ public enum TeradataMetadataConnectorProperties implements ConnectorProperty {
7070
MAX_TEXT_LENGTH(
7171
"max-text-length",
7272
"Max length of the text column when dumping TableTextV view."
73-
+ " Text that is longer than the defined limit will be split."
73+
+ " Text that is longer than the defined limit will be split into multiple rows."
7474
+ " Example: 10000. Allowed range: "
7575
+ MAX_TEXT_LENGTH_RANGE
7676
+ ".");
@@ -263,10 +263,6 @@ private TeradataJdbcSelectTask createTaskForTableTextV(ConnectorArguments argume
263263
TableTextVFormat.ZIP_ENTRY_NAME, TaskCategory.REQUIRED, query);
264264
}
265265

266-
private static <T> Optional<T> optionalIf(boolean condition, Supplier<T> supplier) {
267-
return condition ? Optional.of(supplier.get()) : Optional.empty();
268-
}
269-
270266
private static String escapeStringLiteral(String s) {
271267
return "'" + (s.replaceAll("'", "''")) + "'";
272268
}

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/teradata/TeradataUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@
1818

1919
import com.google.common.base.Preconditions;
2020
import java.util.Optional;
21+
import java.util.function.Supplier;
2122

2223
class TeradataUtils {
2324

25+
public static <T> Optional<T> optionalIf(boolean condition, Supplier<T> supplier) {
26+
return condition ? Optional.of(supplier.get()) : Optional.empty();
27+
}
28+
2429
/**
2530
* Formats the query by:
2631
*

0 commit comments

Comments
 (0)