Skip to content

Commit 4f82b7a

Browse files
committed
database_observability: fetch indexes and foreign keys info
Extend the tableSpec with indexes and foreign keys info.
1 parent d414035 commit 4f82b7a

File tree

2 files changed

+314
-36
lines changed

2 files changed

+314
-36
lines changed

internal/component/database_observability/mysql/collector/schema_table.go

Lines changed: 116 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,35 @@ const (
6464
WHERE
6565
TABLE_SCHEMA = ? AND TABLE_NAME = ?
6666
ORDER BY ORDINAL_POSITION ASC`
67+
68+
selectIndexNames = `
69+
SELECT
70+
index_name,
71+
seq_in_index,
72+
column_name,
73+
nullable,
74+
non_unique,
75+
index_type
76+
FROM
77+
information_schema.statistics
78+
WHERE
79+
table_schema = ? and table_name = ?
80+
ORDER BY table_name, index_name, seq_in_index`
81+
82+
// Ignore 'PRIMARY' constraints, as they're already covered by the query above
83+
selectForeignKeys = `
84+
SELECT
85+
constraint_name,
86+
column_name,
87+
referenced_table_name,
88+
referenced_column_name
89+
FROM
90+
information_schema.key_column_usage
91+
WHERE
92+
constraint_name <> 'PRIMARY'
93+
AND referenced_table_schema is not null
94+
AND table_schema = ? and table_name = ?
95+
ORDER BY table_name, constraint_name, ordinal_position`
6796
)
6897

6998
type SchemaTableArguments struct {
@@ -108,7 +137,9 @@ type tableInfo struct {
108137
}
109138

110139
type tableSpec struct {
111-
Columns []columnSpec `json:"columns"`
140+
Columns []columnSpec `json:"columns"`
141+
Indexes []indexSpec `json:"indexes,omitempty"`
142+
ForeignKeys []foreignKey `json:"foreign_keys,omitempty"`
112143
}
113144
type columnSpec struct {
114145
Name string `json:"name"`
@@ -119,6 +150,21 @@ type columnSpec struct {
119150
DefaultValue string `json:"default_value,omitempty"`
120151
}
121152

153+
type indexSpec struct {
154+
Name string `json:"name"`
155+
Type string `json:"type"`
156+
Columns []string `json:"columns"`
157+
Unique bool `json:"unique"`
158+
Nullable bool `json:"nullable"`
159+
}
160+
161+
type foreignKey struct {
162+
Name string `json:"name"`
163+
ColumnName string `json:"column_name"`
164+
ReferencedTableName string `json:"referenced_table_name"`
165+
ReferencedColumnName string `json:"referenced_column_name"`
166+
}
167+
122168
func NewSchemaTable(args SchemaTableArguments) (*SchemaTable, error) {
123169
c := &SchemaTable{
124170
dbConnection: args.DB,
@@ -375,22 +421,6 @@ func (c *SchemaTable) fetchColumnsDefinitions(ctx context.Context, schemaName st
375421
}
376422

377423
extra = strings.ToUpper(extra)
378-
379-
notNull := false
380-
if isNullable == "NO" {
381-
notNull = true
382-
}
383-
384-
autoIncrement := false
385-
if strings.Contains(extra, "AUTO_INCREMENT") {
386-
autoIncrement = true
387-
}
388-
389-
primaryKey := false
390-
if columnKey == "PRI" {
391-
primaryKey = true
392-
}
393-
394424
defaultValue := ""
395425
if columnDefault.Valid {
396426
defaultValue = columnDefault.String
@@ -402,9 +432,9 @@ func (c *SchemaTable) fetchColumnsDefinitions(ctx context.Context, schemaName st
402432
colSpec := columnSpec{
403433
Name: columnName,
404434
Type: columnType,
405-
NotNull: notNull,
406-
AutoIncrement: autoIncrement,
407-
PrimaryKey: primaryKey,
435+
NotNull: isNullable == "NO",
436+
AutoIncrement: strings.Contains(extra, "AUTO_INCREMENT"),
437+
PrimaryKey: columnKey == "PRI",
408438
DefaultValue: defaultValue,
409439
}
410440
tblSpec.Columns = append(tblSpec.Columns, colSpec)
@@ -415,5 +445,71 @@ func (c *SchemaTable) fetchColumnsDefinitions(ctx context.Context, schemaName st
415445
return nil, err
416446
}
417447

448+
rs, err = c.dbConnection.QueryContext(ctx, selectIndexNames, schemaName, tableName)
449+
if err != nil {
450+
level.Error(c.logger).Log("msg", "failed to query table indexes", "schema", schemaName, "table", tableName, "err", err)
451+
return nil, err
452+
}
453+
defer rs.Close()
454+
455+
for rs.Next() {
456+
var indexName, columnName, indexType string
457+
var seqInIndex, nonUnique int
458+
var nullable sql.NullString
459+
if err := rs.Scan(&indexName, &seqInIndex, &columnName, &nullable, &nonUnique, &indexType); err != nil {
460+
level.Error(c.logger).Log("msg", "failed to scan table indexes", "schema", schemaName, "table", tableName, "err", err)
461+
return nil, err
462+
}
463+
464+
if nIndexes := len(tblSpec.Indexes); nIndexes > 0 && tblSpec.Indexes[nIndexes-1].Name == indexName {
465+
lastIndex := &tblSpec.Indexes[nIndexes-1]
466+
if len(lastIndex.Columns) != seqInIndex-1 {
467+
level.Error(c.logger).Log("msg", "unexpected index column sequence", "schema", schemaName, "table", tableName, "index", indexName, "column", columnName)
468+
continue
469+
}
470+
lastIndex.Columns = append(lastIndex.Columns, columnName)
471+
} else {
472+
tblSpec.Indexes = append(tblSpec.Indexes, indexSpec{
473+
Name: indexName,
474+
Type: indexType,
475+
Columns: []string{columnName},
476+
Unique: nonUnique == 0,
477+
Nullable: nullable.Valid && nullable.String == "YES",
478+
})
479+
}
480+
}
481+
482+
if err := rs.Err(); err != nil {
483+
level.Error(c.logger).Log("msg", "error during iterating over table indexes result set", "schema", schemaName, "table", tableName, "err", err)
484+
return nil, err
485+
}
486+
487+
rs, err = c.dbConnection.QueryContext(ctx, selectForeignKeys, schemaName, tableName)
488+
if err != nil {
489+
level.Error(c.logger).Log("msg", "failed to query table foreign keys", "schema", schemaName, "table", tableName, "err", err)
490+
return nil, err
491+
}
492+
defer rs.Close()
493+
494+
for rs.Next() {
495+
var constraintName, columnName, referencedTableName, referencedColumnName string
496+
if err := rs.Scan(&constraintName, &columnName, &referencedTableName, &referencedColumnName); err != nil {
497+
level.Error(c.logger).Log("msg", "failed to scan foreign keys", "schema", schemaName, "table", tableName, "err", err)
498+
return nil, err
499+
}
500+
501+
tblSpec.ForeignKeys = append(tblSpec.ForeignKeys, foreignKey{
502+
Name: constraintName,
503+
ColumnName: columnName,
504+
ReferencedTableName: referencedTableName,
505+
ReferencedColumnName: referencedColumnName,
506+
})
507+
}
508+
509+
if err := rs.Err(); err != nil {
510+
level.Error(c.logger).Log("msg", "error during iterating over foreign keys result set", "schema", schemaName, "table", tableName, "err", err)
511+
return nil, err
512+
}
513+
418514
return tblSpec, nil
419515
}

0 commit comments

Comments
 (0)