@@ -64,6 +64,35 @@ const (
64
64
WHERE
65
65
TABLE_SCHEMA = ? AND TABLE_NAME = ?
66
66
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`
67
96
)
68
97
69
98
type SchemaTableArguments struct {
@@ -108,7 +137,9 @@ type tableInfo struct {
108
137
}
109
138
110
139
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"`
112
143
}
113
144
type columnSpec struct {
114
145
Name string `json:"name"`
@@ -119,6 +150,21 @@ type columnSpec struct {
119
150
DefaultValue string `json:"default_value,omitempty"`
120
151
}
121
152
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
+
122
168
func NewSchemaTable (args SchemaTableArguments ) (* SchemaTable , error ) {
123
169
c := & SchemaTable {
124
170
dbConnection : args .DB ,
@@ -375,22 +421,6 @@ func (c *SchemaTable) fetchColumnsDefinitions(ctx context.Context, schemaName st
375
421
}
376
422
377
423
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
-
394
424
defaultValue := ""
395
425
if columnDefault .Valid {
396
426
defaultValue = columnDefault .String
@@ -402,9 +432,9 @@ func (c *SchemaTable) fetchColumnsDefinitions(ctx context.Context, schemaName st
402
432
colSpec := columnSpec {
403
433
Name : columnName ,
404
434
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" ,
408
438
DefaultValue : defaultValue ,
409
439
}
410
440
tblSpec .Columns = append (tblSpec .Columns , colSpec )
@@ -415,5 +445,71 @@ func (c *SchemaTable) fetchColumnsDefinitions(ctx context.Context, schemaName st
415
445
return nil , err
416
446
}
417
447
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
+
418
514
return tblSpec , nil
419
515
}
0 commit comments