parent
dae150ea74
commit
8481061a03
|
|
@ -2960,6 +2960,41 @@ func (s *server) executeQuery(opts queryOptions) (queryResult, error) {
|
|||
}
|
||||
|
||||
func (s *server) executeSelect(sqlText string, maxRows int, timeoutSecs int) (queryResult, error) {
|
||||
return executeOracleSelectWithXMLTypeRetry(
|
||||
sqlText,
|
||||
func(query string) (queryResult, error) {
|
||||
return s.executeSelectOnce(query, maxRows, timeoutSecs)
|
||||
},
|
||||
s.rewriteXMLTypeSelectSQL,
|
||||
)
|
||||
}
|
||||
|
||||
func executeOracleSelectWithXMLTypeRetry(
|
||||
sqlText string,
|
||||
execute func(string) (queryResult, error),
|
||||
rewrite func(string) (string, error),
|
||||
) (queryResult, error) {
|
||||
result, err := execute(sqlText)
|
||||
if err == nil || !shouldRetryOracleXMLTypeRewrite(err) {
|
||||
return result, err
|
||||
}
|
||||
rewritten, rewriteErr := rewrite(sqlText)
|
||||
if rewriteErr != nil || rewritten == sqlText {
|
||||
return result, err
|
||||
}
|
||||
return execute(rewritten)
|
||||
}
|
||||
|
||||
func shouldRetryOracleXMLTypeRewrite(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
message := err.Error()
|
||||
return strings.Contains(message, "abnormal data representation for date") ||
|
||||
strings.Contains(message, "TTC error: received code ")
|
||||
}
|
||||
|
||||
func (s *server) executeSelectOnce(sqlText string, maxRows int, timeoutSecs int) (queryResult, error) {
|
||||
rows, err := s.queryRowsWithXMLTypeRewriteIfNeeded(sqlText, timeoutSecs)
|
||||
if err != nil {
|
||||
return queryResult{}, err
|
||||
|
|
|
|||
|
|
@ -274,6 +274,112 @@ func TestNormalizeValueKeepsOracleZonedDateTimeOffsets(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestExecuteOracleSelectRetriesXMLTypeDecodeFailures(t *testing.T) {
|
||||
originalSQL := `SELECT * FROM (SELECT * FROM "DBX"."TEST_LOBS") WHERE ROWNUM <= 100`
|
||||
rewrittenSQL := `SELECT * FROM (SELECT "ID", XMLSERIALIZE(CONTENT "XML_CONTENT" AS CLOB) AS "XML_CONTENT" FROM "DBX"."TEST_LOBS") WHERE ROWNUM <= 100`
|
||||
calls := []string{}
|
||||
|
||||
result, err := executeOracleSelectWithXMLTypeRetry(
|
||||
originalSQL,
|
||||
func(sqlText string) (queryResult, error) {
|
||||
calls = append(calls, sqlText)
|
||||
if sqlText == originalSQL {
|
||||
return queryResult{}, errors.New("abnormal data representation for date")
|
||||
}
|
||||
return queryResult{Columns: []string{"ID", "XML_CONTENT"}, Rows: [][]any{{"1", "<root/>"}}}, nil
|
||||
},
|
||||
func(sqlText string) (string, error) {
|
||||
if sqlText != originalSQL {
|
||||
t.Fatalf("rewrite input = %q, want original SQL", sqlText)
|
||||
}
|
||||
return rewrittenSQL, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(calls, []string{originalSQL, rewrittenSQL}) {
|
||||
t.Fatalf("execute calls = %#v, want original and rewritten SQL", calls)
|
||||
}
|
||||
if len(result.Rows) != 1 || result.Rows[0][1] != "<root/>" {
|
||||
t.Fatalf("unexpected retry result: %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteOracleSelectDoesNotRewriteSuccessfulQueries(t *testing.T) {
|
||||
calls := 0
|
||||
rewriteCalled := false
|
||||
want := queryResult{Columns: []string{"ID"}, Rows: [][]any{{"1"}}}
|
||||
|
||||
result, err := executeOracleSelectWithXMLTypeRetry(
|
||||
`SELECT ID FROM TEST_TABLE`,
|
||||
func(string) (queryResult, error) {
|
||||
calls++
|
||||
return want, nil
|
||||
},
|
||||
func(sqlText string) (string, error) {
|
||||
rewriteCalled = true
|
||||
return sqlText, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, want) {
|
||||
t.Fatalf("result = %#v, want %#v", result, want)
|
||||
}
|
||||
if calls != 1 || rewriteCalled {
|
||||
t.Fatalf("successful query should not rewrite: calls=%d rewriteCalled=%t", calls, rewriteCalled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteOracleSelectDoesNotRetryOrdinaryErrors(t *testing.T) {
|
||||
calls := 0
|
||||
rewriteCalled := false
|
||||
originalErr := errors.New("ORA-00942: table or view does not exist")
|
||||
|
||||
_, err := executeOracleSelectWithXMLTypeRetry(
|
||||
`SELECT * FROM MISSING_TABLE`,
|
||||
func(string) (queryResult, error) {
|
||||
calls++
|
||||
return queryResult{}, originalErr
|
||||
},
|
||||
func(sqlText string) (string, error) {
|
||||
rewriteCalled = true
|
||||
return sqlText, nil
|
||||
},
|
||||
)
|
||||
if !errors.Is(err, originalErr) {
|
||||
t.Fatalf("error = %v, want original error", err)
|
||||
}
|
||||
if calls != 1 || rewriteCalled {
|
||||
t.Fatalf("ordinary error should not retry: calls=%d rewriteCalled=%t", calls, rewriteCalled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteOracleSelectKeepsDecodeErrorWhenNoXMLTypeRewriteApplies(t *testing.T) {
|
||||
calls := 0
|
||||
originalErr := errors.New("TTC error: received code 36 during response reading")
|
||||
sqlText := `SELECT * FROM TEST_DATES`
|
||||
|
||||
_, err := executeOracleSelectWithXMLTypeRetry(
|
||||
sqlText,
|
||||
func(string) (queryResult, error) {
|
||||
calls++
|
||||
return queryResult{}, originalErr
|
||||
},
|
||||
func(input string) (string, error) {
|
||||
return input, nil
|
||||
},
|
||||
)
|
||||
if !errors.Is(err, originalErr) {
|
||||
t.Fatalf("error = %v, want original error", err)
|
||||
}
|
||||
if calls != 1 {
|
||||
t.Fatalf("unchanged SQL should not retry, got %d executions", calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeDDLObjectType(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"": "",
|
||||
|
|
|
|||
Loading…
Reference in New Issue