fix(oracle): avoid eager xmltype metadata lookup

This commit is contained in:
t8y2 2026-07-06 13:37:15 +08:00
parent 5db35307d3
commit ae050dadd0
2 changed files with 66 additions and 16 deletions

View File

@ -1379,11 +1379,7 @@ func (s *server) executeQueryPage(opts queryOptions, pageSize int) (queryPageRes
HasMore: false,
}, err
}
sqlText, err := s.rewriteXMLTypeSelectSQL(sqlText)
if err != nil {
return queryPageResult{}, err
}
rows, err := s.queryRows(sqlText, nil)
rows, err := s.queryRowsWithXMLTypeRewriteIfNeeded(sqlText)
if err != nil {
return queryPageResult{}, err
}
@ -1448,11 +1444,7 @@ func (s *server) startTableRead(opts queryOptions, pageSize int) (queryPageResul
if !isQuerySQL(sqlText) {
return queryPageResult{}, errors.New("table read requires a SELECT query")
}
sqlText, err := s.rewriteXMLTypeSelectSQL(sqlText)
if err != nil {
return queryPageResult{}, err
}
rows, err := s.queryRows(sqlText, nil)
rows, err := s.queryRowsWithXMLTypeRewriteIfNeeded(sqlText)
if err != nil {
return queryPageResult{}, err
}
@ -1603,12 +1595,7 @@ func (s *server) executeQuery(opts queryOptions) (queryResult, error) {
}
func (s *server) executeSelect(sqlText string, maxRows int) (queryResult, error) {
var err error
sqlText, err = s.rewriteXMLTypeSelectSQL(sqlText)
if err != nil {
return queryResult{}, err
}
rows, err := s.queryRows(sqlText, nil)
rows, err := s.queryRowsWithXMLTypeRewriteIfNeeded(sqlText)
if err != nil {
return queryResult{}, err
}
@ -1647,6 +1634,49 @@ func scanRow(rows *sql.Rows, columnCount int) ([]any, error) {
return values, nil
}
func (s *server) queryRowsWithXMLTypeRewriteIfNeeded(sqlText string) (*sql.Rows, error) {
rows, err := s.queryRows(sqlText, nil)
if err != nil {
return nil, err
}
if !rowsContainOracleXMLType(rows) {
return rows, nil
}
rewritten, err := s.rewriteXMLTypeSelectSQL(sqlText)
if err != nil {
rows.Close()
return nil, err
}
if rewritten == sqlText {
return rows, nil
}
// Only pay the ALL_TAB_COLUMNS rewrite cost when the result metadata shows
// XMLTYPE. Ordinary Oracle queries should not run dictionary probes first.
rows.Close()
return s.queryRows(rewritten, nil)
}
func rowsContainOracleXMLType(rows *sql.Rows) bool {
types, err := rows.ColumnTypes()
if err != nil {
return false
}
typeNames := make([]string, 0, len(types))
for _, columnType := range types {
typeNames = append(typeNames, columnType.DatabaseTypeName())
}
return oracleColumnTypeNamesContainXMLType(typeNames)
}
func oracleColumnTypeNamesContainXMLType(typeNames []string) bool {
for _, typeName := range typeNames {
if isOracleXMLType(typeName) {
return true
}
}
return false
}
func (s *server) rewriteXMLTypeSelectSQL(sqlText string) (string, error) {
return rewriteOracleXMLTypeSelectSQL(sqlText, s.loadOracleColumnMeta)
}

View File

@ -514,6 +514,26 @@ func TestRewriteOracleXMLTypeSkipsJoins(t *testing.T) {
}
}
func TestOracleColumnTypeNamesContainXMLType(t *testing.T) {
tests := []struct {
name string
typeNames []string
want bool
}{
{name: "plain xmltype", typeNames: []string{"NUMBER", "XMLTYPE"}, want: true},
{name: "qualified xmltype", typeNames: []string{"SYS.XMLTYPE"}, want: true},
{name: "case and spaces", typeNames: []string{" varchar2 ", "sys.xmltype"}, want: true},
{name: "ordinary columns", typeNames: []string{"NUMBER", "VARCHAR2", "DATE"}, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := oracleColumnTypeNamesContainXMLType(tt.typeNames); got != tt.want {
t.Fatalf("oracleColumnTypeNamesContainXMLType(%v) = %v, want %v", tt.typeNames, got, tt.want)
}
})
}
}
func fakeOracleColumnLoader(columns []oracleColumnMeta) oracleColumnMetaLoader {
return func(schema, table string) ([]oracleColumnMeta, error) {
if strings.ToUpper(table) != "TEST_LOBS" {