parent
a49e795680
commit
147e92ff39
|
|
@ -1956,12 +1956,26 @@ ORDER BY CASE
|
|||
OWNER`, baseSQL, preferredParam, nameColumn, exactParam, typeColumn, nameColumn)
|
||||
}
|
||||
|
||||
func oracleObjectNameCandidates(name string) (string, string, bool) {
|
||||
exact := strings.TrimSpace(name)
|
||||
uppercase := strings.ToUpper(exact)
|
||||
return exact, uppercase, exact != uppercase
|
||||
}
|
||||
|
||||
func (s *server) getColumns(schema, table string) ([]columnInfo, error) {
|
||||
schema, err := s.normalizeSchema(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
table = strings.ToUpper(strings.TrimSpace(table))
|
||||
exact, uppercase, hasUppercaseFallback := oracleObjectNameCandidates(table)
|
||||
result, err := s.getColumnsByName(schema, exact)
|
||||
if err != nil || len(result) > 0 || !hasUppercaseFallback {
|
||||
return result, err
|
||||
}
|
||||
return s.getColumnsByName(schema, uppercase)
|
||||
}
|
||||
|
||||
func (s *server) getColumnsByName(schema, table string) ([]columnInfo, error) {
|
||||
rows, err := s.queryRows(`
|
||||
SELECT c.COLUMN_NAME,
|
||||
c.DATA_TYPE,
|
||||
|
|
@ -2017,7 +2031,15 @@ func (s *server) loadOracleColumnMeta(schema, table string) ([]oracleColumnMeta,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
table = strings.ToUpper(strings.TrimSpace(table))
|
||||
exact, uppercase, hasUppercaseFallback := oracleObjectNameCandidates(table)
|
||||
result, err := s.loadOracleColumnMetaByName(schema, exact)
|
||||
if err != nil || len(result) > 0 || !hasUppercaseFallback {
|
||||
return result, err
|
||||
}
|
||||
return s.loadOracleColumnMetaByName(schema, uppercase)
|
||||
}
|
||||
|
||||
func (s *server) loadOracleColumnMetaByName(schema, table string) ([]oracleColumnMeta, error) {
|
||||
rows, err := s.queryRows(`
|
||||
SELECT COLUMN_NAME, DATA_TYPE
|
||||
FROM ALL_TAB_COLUMNS
|
||||
|
|
@ -2255,7 +2277,7 @@ func (s *server) getTableDDL(schema, table, objectType string) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
objectType, err = s.resolveDDLObjectType(schema, table, objectType)
|
||||
objectType, table, err = s.resolveDDLObject(schema, table, objectType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -2263,7 +2285,7 @@ func (s *server) getTableDDL(schema, table, objectType string) (string, error) {
|
|||
return s.buildViewDDL(schema, table)
|
||||
}
|
||||
var ddl string
|
||||
err = db.QueryRow("SELECT DBMS_METADATA.GET_DDL(:1, :2, :3) FROM DUAL", objectType, strings.ToUpper(table), schema).Scan(&ddl)
|
||||
err = db.QueryRow("SELECT DBMS_METADATA.GET_DDL(:1, :2, :3) FROM DUAL", objectType, table, schema).Scan(&ddl)
|
||||
if err == nil && strings.TrimSpace(ddl) != "" {
|
||||
return ddl, nil
|
||||
}
|
||||
|
|
@ -2273,16 +2295,18 @@ func (s *server) getTableDDL(schema, table, objectType string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
func (s *server) resolveDDLObjectType(schema, name, requested string) (string, error) {
|
||||
func (s *server) resolveDDLObject(schema, name, requested string) (string, string, error) {
|
||||
exact, uppercase, hasUppercaseFallback := oracleObjectNameCandidates(name)
|
||||
objectType := normalizeDDLObjectType(requested)
|
||||
if objectType != "" {
|
||||
return objectType, nil
|
||||
return objectType, exact, nil
|
||||
}
|
||||
db, err := s.requireDB()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
err = db.QueryRow(`
|
||||
resolve := func(objectName string) error {
|
||||
return db.QueryRow(`
|
||||
SELECT OBJECT_TYPE
|
||||
FROM (
|
||||
SELECT OBJECT_TYPE
|
||||
|
|
@ -2292,14 +2316,22 @@ FROM (
|
|||
AND OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')
|
||||
ORDER BY CASE OBJECT_TYPE WHEN 'TABLE' THEN 0 WHEN 'VIEW' THEN 1 ELSE 2 END
|
||||
)
|
||||
WHERE ROWNUM = 1`, schema, strings.ToUpper(name)).Scan(&objectType)
|
||||
WHERE ROWNUM = 1`, schema, objectName).Scan(&objectType)
|
||||
}
|
||||
err = resolve(exact)
|
||||
if errors.Is(err, sql.ErrNoRows) && hasUppercaseFallback {
|
||||
err = resolve(uppercase)
|
||||
if err == nil {
|
||||
exact = uppercase
|
||||
}
|
||||
}
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", fmt.Errorf("object not found: %s.%s", schema, name)
|
||||
return "", "", fmt.Errorf("object not found: %s.%s", schema, name)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
return normalizeDDLObjectType(objectType), nil
|
||||
return normalizeDDLObjectType(objectType), exact, nil
|
||||
}
|
||||
|
||||
func normalizeDDLObjectType(value string) string {
|
||||
|
|
|
|||
|
|
@ -290,6 +290,146 @@ func TestNormalizeDDLObjectType(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetTableDDLPreservesQuotedObjectName(t *testing.T) {
|
||||
const schema = "ZTZS_ERP2"
|
||||
const table = "ZGJ_FlowSealTemplate"
|
||||
const ddl = `CREATE TABLE "ZTZS_ERP2"."ZGJ_FlowSealTemplate" ("FlowId" NUMBER)`
|
||||
db, scripted := openOracleViewSourceTestDB(t, []oracleViewSourceQueryStep{
|
||||
{
|
||||
queryContains: "FROM ALL_OBJECTS",
|
||||
args: []driver.Value{schema, table},
|
||||
rows: [][]driver.Value{{"TABLE"}},
|
||||
},
|
||||
{
|
||||
queryContains: "DBMS_METADATA.GET_DDL",
|
||||
args: []driver.Value{"TABLE", table, schema},
|
||||
rows: [][]driver.Value{{ddl}},
|
||||
},
|
||||
})
|
||||
s := newServer()
|
||||
s.db = db
|
||||
|
||||
got, err := s.getTableDDL(schema, table, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != ddl {
|
||||
t.Fatalf("getTableDDL() = %q, want %q", got, ddl)
|
||||
}
|
||||
if scripted.next != len(scripted.steps) {
|
||||
t.Fatalf("expected %d queries, got %d", len(scripted.steps), scripted.next)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTableDDLUppercaseObjectDoesNotAddFallbackQuery(t *testing.T) {
|
||||
const schema = "ZTZS_ERP2"
|
||||
const table = "ORDERS"
|
||||
const ddl = `CREATE TABLE "ZTZS_ERP2"."ORDERS" ("ID" NUMBER)`
|
||||
db, scripted := openOracleViewSourceTestDB(t, []oracleViewSourceQueryStep{
|
||||
{
|
||||
queryContains: "FROM ALL_OBJECTS",
|
||||
args: []driver.Value{schema, table},
|
||||
rows: [][]driver.Value{{"TABLE"}},
|
||||
},
|
||||
{
|
||||
queryContains: "DBMS_METADATA.GET_DDL",
|
||||
args: []driver.Value{"TABLE", table, schema},
|
||||
rows: [][]driver.Value{{ddl}},
|
||||
},
|
||||
})
|
||||
s := newServer()
|
||||
s.db = db
|
||||
|
||||
got, err := s.getTableDDL(schema, table, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != ddl {
|
||||
t.Fatalf("getTableDDL() = %q, want %q", got, ddl)
|
||||
}
|
||||
if scripted.next != 2 {
|
||||
t.Fatalf("uppercase object should use two queries, got %d", scripted.next)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTableDDLFallsBackToUppercaseAfterExactMiss(t *testing.T) {
|
||||
const schema = "ZTZS_ERP2"
|
||||
const ddl = `CREATE TABLE "ZTZS_ERP2"."ORDERS" ("ID" NUMBER)`
|
||||
db, scripted := openOracleViewSourceTestDB(t, []oracleViewSourceQueryStep{
|
||||
{
|
||||
queryContains: "FROM ALL_OBJECTS",
|
||||
args: []driver.Value{schema, "orders"},
|
||||
rows: nil,
|
||||
},
|
||||
{
|
||||
queryContains: "FROM ALL_OBJECTS",
|
||||
args: []driver.Value{schema, "ORDERS"},
|
||||
rows: [][]driver.Value{{"TABLE"}},
|
||||
},
|
||||
{
|
||||
queryContains: "DBMS_METADATA.GET_DDL",
|
||||
args: []driver.Value{"TABLE", "ORDERS", schema},
|
||||
rows: [][]driver.Value{{ddl}},
|
||||
},
|
||||
})
|
||||
s := newServer()
|
||||
s.db = db
|
||||
|
||||
got, err := s.getTableDDL(schema, "orders", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != ddl {
|
||||
t.Fatalf("getTableDDL() = %q, want %q", got, ddl)
|
||||
}
|
||||
if scripted.next != len(scripted.steps) {
|
||||
t.Fatalf("expected %d queries, got %d", len(scripted.steps), scripted.next)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTableDDLFallbackPreservesQuotedColumnNames(t *testing.T) {
|
||||
const schema = "ZTZS_ERP2"
|
||||
const table = "ZGJ_FlowSealTemplate"
|
||||
db, scripted := openOracleViewSourceTestDB(t, []oracleViewSourceQueryStep{
|
||||
{
|
||||
queryContains: "FROM ALL_OBJECTS",
|
||||
args: []driver.Value{schema, table},
|
||||
rows: [][]driver.Value{{"TABLE"}},
|
||||
},
|
||||
{
|
||||
queryContains: "DBMS_METADATA.GET_DDL",
|
||||
args: []driver.Value{"TABLE", table, schema},
|
||||
err: errors.New("ORA-31603: object not found"),
|
||||
},
|
||||
{
|
||||
queryContains: "FROM ALL_TAB_COLUMNS",
|
||||
args: []driver.Value{schema, table},
|
||||
columns: []string{
|
||||
"COLUMN_NAME", "DATA_TYPE", "NULLABLE", "DATA_DEFAULT", "IS_PRIMARY_KEY",
|
||||
"COMMENTS", "DATA_PRECISION", "DATA_SCALE", "CHAR_LENGTH",
|
||||
},
|
||||
rows: [][]driver.Value{{"FlowId", "VARCHAR2", "N", nil, int64(1), nil, nil, nil, int64(50)}},
|
||||
},
|
||||
})
|
||||
s := newServer()
|
||||
s.db = db
|
||||
|
||||
got, err := s.getTableDDL(schema, table, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := `CREATE TABLE "ZTZS_ERP2"."ZGJ_FlowSealTemplate" (
|
||||
"FlowId" VARCHAR2(50) NOT NULL,
|
||||
PRIMARY KEY ("FlowId")
|
||||
)`
|
||||
if got != want {
|
||||
t.Fatalf("getTableDDL() = %q, want %q", got, want)
|
||||
}
|
||||
if scripted.next != len(scripted.steps) {
|
||||
t.Fatalf("expected %d queries, got %d", len(scripted.steps), scripted.next)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsQuerySQLSkipsLeadingComments(t *testing.T) {
|
||||
tests := []string{
|
||||
"-- 测试\nSELECT * FROM (SELECT * FROM \"DBX_TEST\".\"ORDERS_10K\") WHERE ROWNUM <= 100",
|
||||
|
|
@ -1482,6 +1622,7 @@ func contains(values []string, target string) bool {
|
|||
type oracleViewSourceQueryStep struct {
|
||||
queryContains string
|
||||
args []driver.Value
|
||||
columns []string
|
||||
rows [][]driver.Value
|
||||
err error
|
||||
}
|
||||
|
|
@ -1534,7 +1675,11 @@ func (c *oracleViewSourceConn) QueryContext(
|
|||
if step.err != nil {
|
||||
return nil, step.err
|
||||
}
|
||||
return &oracleViewSourceRows{columns: []string{"SOURCE"}, values: step.rows}, nil
|
||||
columns := step.columns
|
||||
if len(columns) == 0 {
|
||||
columns = []string{"SOURCE"}
|
||||
}
|
||||
return &oracleViewSourceRows{columns: columns, values: step.rows}, nil
|
||||
}
|
||||
|
||||
type oracleViewSourceRows struct {
|
||||
|
|
|
|||
Loading…
Reference in New Issue