From e76c125a6e9ed21b48c0572c9f402d8ad56e3bdc Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sat, 30 Nov 2024 12:58:14 +0100 Subject: [PATCH 1/3] dbinfo: add info for primary keys, indexes, foreign keys Signed-off-by: Rohit Nayak --- go/dbinfo/dbinfo_test.go | 64 ++++++++++++++++++ go/dbinfo/utils.go | 138 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/go/dbinfo/dbinfo_test.go b/go/dbinfo/dbinfo_test.go index 5b84091..6de4ca4 100644 --- a/go/dbinfo/dbinfo_test.go +++ b/go/dbinfo/dbinfo_test.go @@ -144,4 +144,68 @@ func TestDBInfoGet(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, gv) }) + + t.Run("primary keys", func(t *testing.T) { + pks, err := dbh.getPrimaryKeys() + require.NoError(t, err) + require.Len(t, pks, 16) + want := map[string][]string{ + "actor": {"actor_id"}, + "film": {"film_id"}, + "language": {"language_id"}, + "film_category": {"film_id", "category_id"}, + "film_actor": {"actor_id", "film_id"}, + } + for tableName, columns := range want { + pk, ok := pks[tableName] + require.True(t, ok) + require.Equal(t, columns, pk.columns) + } + }) + + t.Run("indexes", func(t *testing.T) { + idxs, err := dbh.getIndexes() + require.NoError(t, err) + require.Equal(t, 16, idxs.len()) + idx, ok := idxs["film_actor"] + require.True(t, ok) + require.Len(t, idx.indexes, 2) + require.Equal(t, "idx_fk_film_id", idx.indexes["idx_fk_film_id"].indexName) + require.Equal(t, []string{"film_id"}, idx.indexes["idx_fk_film_id"].columns) + idx, ok = idxs["rental"] + require.True(t, ok) + require.Len(t, idx.indexes, 5) + require.Equal(t, "rental_date", idx.indexes["rental_date"].indexName) + require.Equal(t, []string{"rental_date", "inventory_id", "customer_id"}, idx.indexes["rental_date"].columns) + require.Equal(t, "PRIMARY", idx.indexes["PRIMARY_KEY"].indexName) + require.Equal(t, []string{"rental_id"}, idx.indexes["PRIMARY_KEY"].columns) + }) + + t.Run("foreign keys", func(t *testing.T) { + fks, err := dbh.getForeignKeys() + require.NoError(t, err) + require.Len(t, fks, 11) + fk, ok := fks["city"] + require.True(t, ok) + require.Len(t, fk, 1) + require.Equal(t, "city", fk[0].tableName) + require.Equal(t, "country_id", fk[0].columnName) + require.Equal(t, "fk_city_country", fk[0].constraintName) + require.Equal(t, "country", fk[0].referencedTableName) + require.Equal(t, "country_id", fk[0].referencedColumnName) + + fk, ok = fks["store"] + require.True(t, ok) + require.Len(t, fk, 2) + require.Equal(t, "store", fk[0].tableName) + require.Equal(t, "address_id", fk[0].columnName) + require.Equal(t, "fk_store_address", fk[0].constraintName) + require.Equal(t, "address", fk[0].referencedTableName) + require.Equal(t, "address_id", fk[0].referencedColumnName) + require.Equal(t, "store", fk[1].tableName) + require.Equal(t, "manager_staff_id", fk[1].columnName) + require.Equal(t, "fk_store_staff", fk[1].constraintName) + require.Equal(t, "staff", fk[1].referencedTableName) + require.Equal(t, "staff_id", fk[1].referencedColumnName) + }) } diff --git a/go/dbinfo/utils.go b/go/dbinfo/utils.go index 8b23ab9..f2d17ff 100644 --- a/go/dbinfo/utils.go +++ b/go/dbinfo/utils.go @@ -130,3 +130,141 @@ func (dbh *DBHelper) getGlobalVariables() (map[string]string, error) { } return gv, nil } + +type primaryKey struct { + tableName string + columns []string +} +type primaryKeys map[string]*primaryKey + +func (dbh *DBHelper) getPrimaryKeys() (primaryKeys, error) { + vtConn, cancel, err := dbh.GetConnection() + if err != nil { + return nil, err + } + defer cancel() + + pks := make(primaryKeys) + queryPrimaryKeys := "select table_name, column_name from information_schema.key_column_usage where constraint_name = 'PRIMARY' and table_schema = '%s' order by table_name" + query := fmt.Sprintf(queryPrimaryKeys, dbh.vtParams.DbName) + qr, err := vtConn.ExecuteFetch(query, -1, false) + if err != nil { + return nil, err + } + for _, row := range qr.Rows { + tableName := row[0].ToString() + columnName := row[1].ToString() + pk, ok := pks[tableName] + if !ok { + pk = &primaryKey{tableName: tableName} + pks[tableName] = pk + } + pk.columns = append(pk.columns, columnName) + } + return pks, nil +} + +type tableIndex struct { + tableName string + indexes map[string]*index +} +type index struct { + indexName string + columns []string + nonUnique bool +} + +type indexes map[string]*tableIndex + +func (i *indexes) len() int { + return len(*i) +} + +func (dbh *DBHelper) getIndexes() (indexes, error) { + vtConn, cancel, err := dbh.GetConnection() + if err != nil { + return nil, err + } + defer cancel() + + idxs := make(indexes) + queryIndexes := "select table_name, index_name, column_name, non_unique from information_schema.statistics where table_schema = '%s' order by table_name, index_name" + query := fmt.Sprintf(queryIndexes, dbh.vtParams.DbName) + qr, err := vtConn.ExecuteFetch(query, -1, false) + if err != nil { + return nil, err + } + for _, row := range qr.Rows { + tableName := row[0].ToString() + indexName := row[1].ToString() + columnName := row[2].ToString() + nonUnique, _ := row[3].ToBool() + tidx, ok := idxs[tableName] + if !ok { + tidx = &tableIndex{tableName: tableName, indexes: make(map[string]*index)} + idxs[tableName] = tidx + } + idxName := indexName + if idxName == "PRIMARY" { + idxName = "PRIMARY_KEY" + } + idx, ok := tidx.indexes[idxName] + if !ok { + idx = &index{indexName: indexName, nonUnique: nonUnique} + tidx.indexes[idxName] = idx + } + idx.columns = append(idx.columns, columnName) + } + return idxs, nil +} + +// Foreign Keys with Dependency Information +// SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME +// FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE +// WHERE TABLE_SCHEMA = 'your_database_name' AND REFERENCED_TABLE_NAME IS NOT NULL; + +type foreignKey struct { + tableName string + columnName string + constraintName string + referencedTableName string + referencedColumnName string +} + +func (fk *foreignKey) String() string { + return fmt.Sprintf("Table: %s, Column: %s, Constraint: %s, RefTable: %s, RefColumn: %s", fk.tableName, fk.columnName, fk.constraintName, fk.referencedTableName, fk.referencedColumnName) +} + +type foreignKeys map[string][]*foreignKey + +func (dbh *DBHelper) getForeignKeys() (foreignKeys, error) { + vtConn, cancel, err := dbh.GetConnection() + if err != nil { + return nil, err + } + defer cancel() + + fks := make(foreignKeys) + queryForeignKeys := "select table_name, column_name, constraint_name, referenced_table_name, referenced_column_name from information_schema.key_column_usage where table_schema = '%s' and referenced_table_name is not null" + query := fmt.Sprintf(queryForeignKeys, dbh.vtParams.DbName) + qr, err := vtConn.ExecuteFetch(query, -1, false) + if err != nil { + return nil, err + } + for _, row := range qr.Rows { + tableName := row[0].ToString() + columnName := row[1].ToString() + constraintName := row[2].ToString() + referencedTableName := row[3].ToString() + referencedColumnName := row[4].ToString() + fk := &foreignKey{ + tableName: tableName, + columnName: columnName, + constraintName: constraintName, + referencedTableName: referencedTableName, + referencedColumnName: referencedColumnName, + } + fks[tableName] = append(fks[tableName], fk) + } + return fks, nil +} From ee3ed63041b0b155e6c4b7061fc9ea4e7a783d01 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sun, 1 Dec 2024 07:22:34 +0100 Subject: [PATCH 2/3] Add new elements to returned dbinfo struct Signed-off-by: Rohit Nayak --- go/dbinfo/dbinfo.go | 21 ++++++++++ go/dbinfo/dbinfo_test.go | 50 ++++++++++++------------ go/dbinfo/utils.go | 83 ++++++++++++++++++++-------------------- 3 files changed, 88 insertions(+), 66 deletions(-) diff --git a/go/dbinfo/dbinfo.go b/go/dbinfo/dbinfo.go index 297272a..442e0f0 100644 --- a/go/dbinfo/dbinfo.go +++ b/go/dbinfo/dbinfo.go @@ -63,6 +63,9 @@ type Info struct { FileType string `json:"fileType"` Tables []*TableInfo `json:"tables"` GlobalVariables map[string]string `json:"globalVariables"` + PrimaryKeys PrimaryKeys `json:"primaryKeys"` + Indexes Indexes `json:"indexes"` + ForeignKeys ForeignKeys `json:"foreignKeys"` } func Get(cfg Config) (*Info, error) { @@ -115,10 +118,28 @@ func Get(cfg Config) (*Info, error) { return nil, err } + primaryKeys, err := dbh.getPrimaryKeys() + if err != nil { + return nil, err + } + + indexes, err := dbh.getIndexes() + if err != nil { + return nil, err + } + + foreignKeys, err := dbh.getForeignKeys() + if err != nil { + return nil, err + } + dbInfo := &Info{ FileType: "dbinfo", Tables: tableInfo, GlobalVariables: globalVariables, + PrimaryKeys: primaryKeys, + Indexes: indexes, + ForeignKeys: foreignKeys, } return dbInfo, nil } diff --git a/go/dbinfo/dbinfo_test.go b/go/dbinfo/dbinfo_test.go index 6de4ca4..14ceedf 100644 --- a/go/dbinfo/dbinfo_test.go +++ b/go/dbinfo/dbinfo_test.go @@ -156,10 +156,10 @@ func TestDBInfoGet(t *testing.T) { "film_category": {"film_id", "category_id"}, "film_actor": {"actor_id", "film_id"}, } - for tableName, columns := range want { + for tableName, Columns := range want { pk, ok := pks[tableName] require.True(t, ok) - require.Equal(t, columns, pk.columns) + require.Equal(t, Columns, pk.Columns) } }) @@ -169,16 +169,16 @@ func TestDBInfoGet(t *testing.T) { require.Equal(t, 16, idxs.len()) idx, ok := idxs["film_actor"] require.True(t, ok) - require.Len(t, idx.indexes, 2) - require.Equal(t, "idx_fk_film_id", idx.indexes["idx_fk_film_id"].indexName) - require.Equal(t, []string{"film_id"}, idx.indexes["idx_fk_film_id"].columns) + require.Len(t, idx.Indexes, 2) + require.Equal(t, "idx_fk_film_id", idx.Indexes["idx_fk_film_id"].IndexName) + require.Equal(t, []string{"film_id"}, idx.Indexes["idx_fk_film_id"].Columns) idx, ok = idxs["rental"] require.True(t, ok) - require.Len(t, idx.indexes, 5) - require.Equal(t, "rental_date", idx.indexes["rental_date"].indexName) - require.Equal(t, []string{"rental_date", "inventory_id", "customer_id"}, idx.indexes["rental_date"].columns) - require.Equal(t, "PRIMARY", idx.indexes["PRIMARY_KEY"].indexName) - require.Equal(t, []string{"rental_id"}, idx.indexes["PRIMARY_KEY"].columns) + require.Len(t, idx.Indexes, 5) + require.Equal(t, "rental_date", idx.Indexes["rental_date"].IndexName) + require.Equal(t, []string{"rental_date", "inventory_id", "customer_id"}, idx.Indexes["rental_date"].Columns) + require.Equal(t, "PRIMARY", idx.Indexes["PRIMARY_KEY"].IndexName) + require.Equal(t, []string{"rental_id"}, idx.Indexes["PRIMARY_KEY"].Columns) }) t.Run("foreign keys", func(t *testing.T) { @@ -188,24 +188,24 @@ func TestDBInfoGet(t *testing.T) { fk, ok := fks["city"] require.True(t, ok) require.Len(t, fk, 1) - require.Equal(t, "city", fk[0].tableName) - require.Equal(t, "country_id", fk[0].columnName) - require.Equal(t, "fk_city_country", fk[0].constraintName) - require.Equal(t, "country", fk[0].referencedTableName) - require.Equal(t, "country_id", fk[0].referencedColumnName) + require.Equal(t, "city", fk[0].TableName) + require.Equal(t, "country_id", fk[0].ColumnName) + require.Equal(t, "fk_city_country", fk[0].ConstraintName) + require.Equal(t, "country", fk[0].ReferencedTableName) + require.Equal(t, "country_id", fk[0].ReferencedColumnName) fk, ok = fks["store"] require.True(t, ok) require.Len(t, fk, 2) - require.Equal(t, "store", fk[0].tableName) - require.Equal(t, "address_id", fk[0].columnName) - require.Equal(t, "fk_store_address", fk[0].constraintName) - require.Equal(t, "address", fk[0].referencedTableName) - require.Equal(t, "address_id", fk[0].referencedColumnName) - require.Equal(t, "store", fk[1].tableName) - require.Equal(t, "manager_staff_id", fk[1].columnName) - require.Equal(t, "fk_store_staff", fk[1].constraintName) - require.Equal(t, "staff", fk[1].referencedTableName) - require.Equal(t, "staff_id", fk[1].referencedColumnName) + require.Equal(t, "store", fk[0].TableName) + require.Equal(t, "address_id", fk[0].ColumnName) + require.Equal(t, "fk_store_address", fk[0].ConstraintName) + require.Equal(t, "address", fk[0].ReferencedTableName) + require.Equal(t, "address_id", fk[0].ReferencedColumnName) + require.Equal(t, "store", fk[1].TableName) + require.Equal(t, "manager_staff_id", fk[1].ColumnName) + require.Equal(t, "fk_store_staff", fk[1].ConstraintName) + require.Equal(t, "staff", fk[1].ReferencedTableName) + require.Equal(t, "staff_id", fk[1].ReferencedColumnName) }) } diff --git a/go/dbinfo/utils.go b/go/dbinfo/utils.go index f2d17ff..f82b9b5 100644 --- a/go/dbinfo/utils.go +++ b/go/dbinfo/utils.go @@ -131,20 +131,20 @@ func (dbh *DBHelper) getGlobalVariables() (map[string]string, error) { return gv, nil } -type primaryKey struct { - tableName string - columns []string +type PrimaryKey struct { + TableName string `json:"tableName"` + Columns []string `json:"columns"` } -type primaryKeys map[string]*primaryKey +type PrimaryKeys map[string]*PrimaryKey -func (dbh *DBHelper) getPrimaryKeys() (primaryKeys, error) { +func (dbh *DBHelper) getPrimaryKeys() (PrimaryKeys, error) { vtConn, cancel, err := dbh.GetConnection() if err != nil { return nil, err } defer cancel() - pks := make(primaryKeys) + pks := make(PrimaryKeys) queryPrimaryKeys := "select table_name, column_name from information_schema.key_column_usage where constraint_name = 'PRIMARY' and table_schema = '%s' order by table_name" query := fmt.Sprintf(queryPrimaryKeys, dbh.vtParams.DbName) qr, err := vtConn.ExecuteFetch(query, -1, false) @@ -156,38 +156,39 @@ func (dbh *DBHelper) getPrimaryKeys() (primaryKeys, error) { columnName := row[1].ToString() pk, ok := pks[tableName] if !ok { - pk = &primaryKey{tableName: tableName} + pk = &PrimaryKey{TableName: tableName} pks[tableName] = pk } - pk.columns = append(pk.columns, columnName) + pk.Columns = append(pk.Columns, columnName) } return pks, nil } -type tableIndex struct { - tableName string - indexes map[string]*index +type TableIndex struct { + TableName string `json:"tableName"` + Indexes map[string]*Index `json:"indexes"` } -type index struct { - indexName string - columns []string - nonUnique bool + +type Index struct { + IndexName string `json:"indexName"` + Columns []string `json:"columns"` + NonUnique bool `json:"nonUnique,omitempty"` } -type indexes map[string]*tableIndex +type Indexes map[string]*TableIndex -func (i *indexes) len() int { +func (i *Indexes) len() int { return len(*i) } -func (dbh *DBHelper) getIndexes() (indexes, error) { +func (dbh *DBHelper) getIndexes() (Indexes, error) { vtConn, cancel, err := dbh.GetConnection() if err != nil { return nil, err } defer cancel() - idxs := make(indexes) + idxs := make(Indexes) queryIndexes := "select table_name, index_name, column_name, non_unique from information_schema.statistics where table_schema = '%s' order by table_name, index_name" query := fmt.Sprintf(queryIndexes, dbh.vtParams.DbName) qr, err := vtConn.ExecuteFetch(query, -1, false) @@ -201,19 +202,19 @@ func (dbh *DBHelper) getIndexes() (indexes, error) { nonUnique, _ := row[3].ToBool() tidx, ok := idxs[tableName] if !ok { - tidx = &tableIndex{tableName: tableName, indexes: make(map[string]*index)} + tidx = &TableIndex{TableName: tableName, Indexes: make(map[string]*Index)} idxs[tableName] = tidx } idxName := indexName if idxName == "PRIMARY" { idxName = "PRIMARY_KEY" } - idx, ok := tidx.indexes[idxName] + idx, ok := tidx.Indexes[idxName] if !ok { - idx = &index{indexName: indexName, nonUnique: nonUnique} - tidx.indexes[idxName] = idx + idx = &Index{IndexName: indexName, NonUnique: nonUnique} + tidx.Indexes[idxName] = idx } - idx.columns = append(idx.columns, columnName) + idx.Columns = append(idx.Columns, columnName) } return idxs, nil } @@ -223,28 +224,28 @@ func (dbh *DBHelper) getIndexes() (indexes, error) { // FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE // WHERE TABLE_SCHEMA = 'your_database_name' AND REFERENCED_TABLE_NAME IS NOT NULL; -type foreignKey struct { - tableName string - columnName string - constraintName string - referencedTableName string - referencedColumnName string +type ForeignKey struct { + TableName string `json:"tableName"` + ColumnName string `json:"columnName"` + ConstraintName string `json:"constraintName"` + ReferencedTableName string `json:"referencedTableName"` + ReferencedColumnName string `json:"referencedColumnName"` } -func (fk *foreignKey) String() string { - return fmt.Sprintf("Table: %s, Column: %s, Constraint: %s, RefTable: %s, RefColumn: %s", fk.tableName, fk.columnName, fk.constraintName, fk.referencedTableName, fk.referencedColumnName) +func (fk *ForeignKey) String() string { + return fmt.Sprintf("Table: %s, Column: %s, Constraint: %s, RefTable: %s, RefColumn: %s", fk.TableName, fk.ColumnName, fk.ConstraintName, fk.ReferencedTableName, fk.ReferencedColumnName) } -type foreignKeys map[string][]*foreignKey +type ForeignKeys map[string][]*ForeignKey -func (dbh *DBHelper) getForeignKeys() (foreignKeys, error) { +func (dbh *DBHelper) getForeignKeys() (ForeignKeys, error) { vtConn, cancel, err := dbh.GetConnection() if err != nil { return nil, err } defer cancel() - fks := make(foreignKeys) + fks := make(ForeignKeys) queryForeignKeys := "select table_name, column_name, constraint_name, referenced_table_name, referenced_column_name from information_schema.key_column_usage where table_schema = '%s' and referenced_table_name is not null" query := fmt.Sprintf(queryForeignKeys, dbh.vtParams.DbName) qr, err := vtConn.ExecuteFetch(query, -1, false) @@ -257,12 +258,12 @@ func (dbh *DBHelper) getForeignKeys() (foreignKeys, error) { constraintName := row[2].ToString() referencedTableName := row[3].ToString() referencedColumnName := row[4].ToString() - fk := &foreignKey{ - tableName: tableName, - columnName: columnName, - constraintName: constraintName, - referencedTableName: referencedTableName, - referencedColumnName: referencedColumnName, + fk := &ForeignKey{ + TableName: tableName, + ColumnName: columnName, + ConstraintName: constraintName, + ReferencedTableName: referencedTableName, + ReferencedColumnName: referencedColumnName, } fks[tableName] = append(fks[tableName], fk) } From 87a74847899441a80bceb371830d832dfd227b40 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Mon, 2 Dec 2024 12:19:41 +0100 Subject: [PATCH 3/3] Move index and fk info into per-table struct Signed-off-by: Rohit Nayak --- go/dbinfo/dbinfo.go | 148 ++- go/dbinfo/dbinfo_test.go | 26 +- go/dbinfo/utils.go | 82 +- go/testdata/sakila-dbinfo.json | 1611 +++++++++++++++++++------------- 4 files changed, 1078 insertions(+), 789 deletions(-) diff --git a/go/dbinfo/dbinfo.go b/go/dbinfo/dbinfo.go index 442e0f0..8102f75 100644 --- a/go/dbinfo/dbinfo.go +++ b/go/dbinfo/dbinfo.go @@ -20,6 +20,7 @@ import ( "encoding/json" "io" "os" + "sort" "vitess.io/vitess/go/mysql" ) @@ -53,93 +54,158 @@ type TableColumn struct { Extra string `json:"extra,omitempty"` } +type PrimaryKey struct { + Columns []string `json:"columns"` +} + +type Index struct { + Name string + Columns []string `json:"columns"` + NonUnique bool `json:"nonUnique,omitempty"` +} + +type ForeignKey struct { + ColumnName string `json:"columnName"` + ConstraintName string `json:"constraintName"` + ReferencedTableName string `json:"referencedTableName"` + ReferencedColumnName string `json:"referencedColumnName"` +} + type TableInfo struct { - Name string `json:"name"` - Rows int `json:"rows"` - Columns []*TableColumn `json:"columns"` + Name string `json:"name"` + Rows int `json:"rows"` + Columns []*TableColumn `json:"columns"` + PrimaryKey *PrimaryKey `json:"primaryKey,omitempty"` + Indexes []*Index `json:"indexes,omitempty"` + ForeignKeys []*ForeignKey `json:"foreignKeys,omitempty"` } type Info struct { FileType string `json:"fileType"` Tables []*TableInfo `json:"tables"` GlobalVariables map[string]string `json:"globalVariables"` - PrimaryKeys PrimaryKeys `json:"primaryKeys"` - Indexes Indexes `json:"indexes"` - ForeignKeys ForeignKeys `json:"foreignKeys"` } -func Get(cfg Config) (*Info, error) { - vtParams := &mysql.ConnParams{ - Host: cfg.VTParams.Host, - Port: cfg.VTParams.Port, - Uname: cfg.VTParams.Uname, - Pass: cfg.VTParams.Pass, - DbName: cfg.VTParams.DbName, - } - - dbh := NewDBHelper(vtParams) +func getTableSizes(dbh *DBHelper, tableMap map[string]*TableInfo) error { ts, err := dbh.getTableSizes() if err != nil { - return nil, err + return err } - var tableInfo []*TableInfo - tableMap := make(map[string]*TableInfo) - for tableName, tableRows := range ts { - tableMap[tableName] = &TableInfo{ - Name: tableName, - Rows: tableRows, + ti, ok := tableMap[tableName] + if !ok { + ti = &TableInfo{ + Name: tableName, + } + tableMap[tableName] = ti } + ti.Rows = tableRows } + return nil +} - tc, err := dbh.getColumnInfo() +func getPrimaryKeys(dbh *DBHelper, tableMap map[string]*TableInfo) error { + pks, err := dbh.getPrimaryKeys() if err != nil { - return nil, err + return err } - for tableName, columns := range tc { + for tableName, pk := range pks { ti, ok := tableMap[tableName] if !ok { - ti = &TableInfo{ - Name: tableName, - } + ti = &TableInfo{} tableMap[tableName] = ti } - ti.Columns = columns + ti.PrimaryKey = &PrimaryKey{ + Columns: pk.columns, + } } + return nil +} - for tableName := range tableMap { - tableInfo = append(tableInfo, tableMap[tableName]) +func getIndexes(dbh *DBHelper, tableMap map[string]*TableInfo) error { + idxs, err := dbh.getIndexes() + if err != nil { + return err + } + + for tableName, tidx := range idxs { + ti, ok := tableMap[tableName] + if !ok { + ti = &TableInfo{} + tableMap[tableName] = ti + } + for _, idx := range tidx.indexes { + ti.Indexes = append(ti.Indexes, idx) + } + } + return nil +} + +func getForeignKeys(dbh *DBHelper, tableMap map[string]*TableInfo) error { + fks, err := dbh.getForeignKeys() + if err != nil { + return err + } + + for fkName, fk := range fks { + ti, ok := tableMap[fkName] + if !ok { + ti = &TableInfo{} + tableMap[fkName] = ti + } + ti.ForeignKeys = fk + } + return nil +} + +func Get(cfg Config) (*Info, error) { + vtParams := &mysql.ConnParams{ + Host: cfg.VTParams.Host, + Port: cfg.VTParams.Port, + Uname: cfg.VTParams.Uname, + Pass: cfg.VTParams.Pass, + DbName: cfg.VTParams.DbName, } + var tableInfo []*TableInfo + tableMap := make(map[string]*TableInfo) + + dbh := NewDBHelper(vtParams) + globalVariables, err := dbh.getGlobalVariables() if err != nil { return nil, err } - primaryKeys, err := dbh.getPrimaryKeys() - if err != nil { + if err := getTableSizes(dbh, tableMap); err != nil { return nil, err } - indexes, err := dbh.getIndexes() - if err != nil { + if err := getPrimaryKeys(dbh, tableMap); err != nil { return nil, err } - foreignKeys, err := dbh.getForeignKeys() - if err != nil { + if err := getIndexes(dbh, tableMap); err != nil { + return nil, err + } + + if err := getForeignKeys(dbh, tableMap); err != nil { return nil, err } + for tableName := range tableMap { + tableInfo = append(tableInfo, tableMap[tableName]) + } + sort.Slice(tableInfo, func(i, j int) bool { + return tableInfo[i].Name < tableInfo[j].Name + }) + dbInfo := &Info{ FileType: "dbinfo", Tables: tableInfo, GlobalVariables: globalVariables, - PrimaryKeys: primaryKeys, - Indexes: indexes, - ForeignKeys: foreignKeys, } return dbInfo, nil } diff --git a/go/dbinfo/dbinfo_test.go b/go/dbinfo/dbinfo_test.go index 14ceedf..9fa37d0 100644 --- a/go/dbinfo/dbinfo_test.go +++ b/go/dbinfo/dbinfo_test.go @@ -54,10 +54,11 @@ func TestDBInfoLoad(t *testing.T) { t.Run("validateGlobalVariables", func(t *testing.T) { require.NotEmpty(t, si.GlobalVariables) - require.Len(t, si.GlobalVariables, 3) + require.Len(t, si.GlobalVariables, 4) expected := map[string]string{ "binlog_format": "ROW", "binlog_row_image": "FULL", + "gtid_mode": "OFF", "log_bin": "ON", } require.EqualValues(t, expected, si.GlobalVariables) @@ -159,26 +160,26 @@ func TestDBInfoGet(t *testing.T) { for tableName, Columns := range want { pk, ok := pks[tableName] require.True(t, ok) - require.Equal(t, Columns, pk.Columns) + require.Equal(t, Columns, pk.columns) } }) t.Run("indexes", func(t *testing.T) { idxs, err := dbh.getIndexes() require.NoError(t, err) - require.Equal(t, 16, idxs.len()) + require.Len(t, idxs, 16) idx, ok := idxs["film_actor"] require.True(t, ok) - require.Len(t, idx.Indexes, 2) - require.Equal(t, "idx_fk_film_id", idx.Indexes["idx_fk_film_id"].IndexName) - require.Equal(t, []string{"film_id"}, idx.Indexes["idx_fk_film_id"].Columns) + require.Len(t, idx.indexes, 2) + require.Equal(t, "idx_fk_film_id", idx.indexes["idx_fk_film_id"].Name) + require.Equal(t, []string{"film_id"}, idx.indexes["idx_fk_film_id"].Columns) idx, ok = idxs["rental"] require.True(t, ok) - require.Len(t, idx.Indexes, 5) - require.Equal(t, "rental_date", idx.Indexes["rental_date"].IndexName) - require.Equal(t, []string{"rental_date", "inventory_id", "customer_id"}, idx.Indexes["rental_date"].Columns) - require.Equal(t, "PRIMARY", idx.Indexes["PRIMARY_KEY"].IndexName) - require.Equal(t, []string{"rental_id"}, idx.Indexes["PRIMARY_KEY"].Columns) + require.Len(t, idx.indexes, 5) + require.Equal(t, "rental_date", idx.indexes["rental_date"].Name) + require.Equal(t, []string{"rental_date", "inventory_id", "customer_id"}, idx.indexes["rental_date"].Columns) + require.Equal(t, "PRIMARY", idx.indexes["PRIMARY_KEY"].Name) + require.Equal(t, []string{"rental_id"}, idx.indexes["PRIMARY_KEY"].Columns) }) t.Run("foreign keys", func(t *testing.T) { @@ -188,7 +189,6 @@ func TestDBInfoGet(t *testing.T) { fk, ok := fks["city"] require.True(t, ok) require.Len(t, fk, 1) - require.Equal(t, "city", fk[0].TableName) require.Equal(t, "country_id", fk[0].ColumnName) require.Equal(t, "fk_city_country", fk[0].ConstraintName) require.Equal(t, "country", fk[0].ReferencedTableName) @@ -197,12 +197,10 @@ func TestDBInfoGet(t *testing.T) { fk, ok = fks["store"] require.True(t, ok) require.Len(t, fk, 2) - require.Equal(t, "store", fk[0].TableName) require.Equal(t, "address_id", fk[0].ColumnName) require.Equal(t, "fk_store_address", fk[0].ConstraintName) require.Equal(t, "address", fk[0].ReferencedTableName) require.Equal(t, "address_id", fk[0].ReferencedColumnName) - require.Equal(t, "store", fk[1].TableName) require.Equal(t, "manager_staff_id", fk[1].ColumnName) require.Equal(t, "fk_store_staff", fk[1].ConstraintName) require.Equal(t, "staff", fk[1].ReferencedTableName) diff --git a/go/dbinfo/utils.go b/go/dbinfo/utils.go index f82b9b5..8255d46 100644 --- a/go/dbinfo/utils.go +++ b/go/dbinfo/utils.go @@ -131,20 +131,20 @@ func (dbh *DBHelper) getGlobalVariables() (map[string]string, error) { return gv, nil } -type PrimaryKey struct { - TableName string `json:"tableName"` - Columns []string `json:"columns"` +type primaryKey struct { + tableName string + columns []string } -type PrimaryKeys map[string]*PrimaryKey +type primaryKeys map[string]*primaryKey -func (dbh *DBHelper) getPrimaryKeys() (PrimaryKeys, error) { +func (dbh *DBHelper) getPrimaryKeys() (primaryKeys, error) { vtConn, cancel, err := dbh.GetConnection() if err != nil { return nil, err } defer cancel() - pks := make(PrimaryKeys) + pks := make(primaryKeys) queryPrimaryKeys := "select table_name, column_name from information_schema.key_column_usage where constraint_name = 'PRIMARY' and table_schema = '%s' order by table_name" query := fmt.Sprintf(queryPrimaryKeys, dbh.vtParams.DbName) qr, err := vtConn.ExecuteFetch(query, -1, false) @@ -156,39 +156,27 @@ func (dbh *DBHelper) getPrimaryKeys() (PrimaryKeys, error) { columnName := row[1].ToString() pk, ok := pks[tableName] if !ok { - pk = &PrimaryKey{TableName: tableName} + pk = &primaryKey{tableName: tableName} pks[tableName] = pk } - pk.Columns = append(pk.Columns, columnName) + pk.columns = append(pk.columns, columnName) } return pks, nil } -type TableIndex struct { - TableName string `json:"tableName"` - Indexes map[string]*Index `json:"indexes"` +type tableIndex struct { + tableName string + indexes map[string]*Index } -type Index struct { - IndexName string `json:"indexName"` - Columns []string `json:"columns"` - NonUnique bool `json:"nonUnique,omitempty"` -} - -type Indexes map[string]*TableIndex - -func (i *Indexes) len() int { - return len(*i) -} - -func (dbh *DBHelper) getIndexes() (Indexes, error) { +func (dbh *DBHelper) getIndexes() (map[string]*tableIndex, error) { vtConn, cancel, err := dbh.GetConnection() if err != nil { return nil, err } defer cancel() - idxs := make(Indexes) + idxs := make(map[string]*tableIndex) queryIndexes := "select table_name, index_name, column_name, non_unique from information_schema.statistics where table_schema = '%s' order by table_name, index_name" query := fmt.Sprintf(queryIndexes, dbh.vtParams.DbName) qr, err := vtConn.ExecuteFetch(query, -1, false) @@ -202,50 +190,31 @@ func (dbh *DBHelper) getIndexes() (Indexes, error) { nonUnique, _ := row[3].ToBool() tidx, ok := idxs[tableName] if !ok { - tidx = &TableIndex{TableName: tableName, Indexes: make(map[string]*Index)} + tidx = &tableIndex{tableName: tableName, indexes: make(map[string]*Index)} idxs[tableName] = tidx } idxName := indexName if idxName == "PRIMARY" { idxName = "PRIMARY_KEY" } - idx, ok := tidx.Indexes[idxName] + idx, ok := tidx.indexes[idxName] if !ok { - idx = &Index{IndexName: indexName, NonUnique: nonUnique} - tidx.Indexes[idxName] = idx + idx = &Index{Name: indexName, NonUnique: nonUnique} + tidx.indexes[idxName] = idx } idx.Columns = append(idx.Columns, columnName) } return idxs, nil } -// Foreign Keys with Dependency Information -// SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME -// FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -// WHERE TABLE_SCHEMA = 'your_database_name' AND REFERENCED_TABLE_NAME IS NOT NULL; - -type ForeignKey struct { - TableName string `json:"tableName"` - ColumnName string `json:"columnName"` - ConstraintName string `json:"constraintName"` - ReferencedTableName string `json:"referencedTableName"` - ReferencedColumnName string `json:"referencedColumnName"` -} - -func (fk *ForeignKey) String() string { - return fmt.Sprintf("Table: %s, Column: %s, Constraint: %s, RefTable: %s, RefColumn: %s", fk.TableName, fk.ColumnName, fk.ConstraintName, fk.ReferencedTableName, fk.ReferencedColumnName) -} - -type ForeignKeys map[string][]*ForeignKey - -func (dbh *DBHelper) getForeignKeys() (ForeignKeys, error) { +func (dbh *DBHelper) getForeignKeys() (map[string][]*ForeignKey, error) { vtConn, cancel, err := dbh.GetConnection() if err != nil { return nil, err } defer cancel() - fks := make(ForeignKeys) + fks := make(map[string][]*ForeignKey) queryForeignKeys := "select table_name, column_name, constraint_name, referenced_table_name, referenced_column_name from information_schema.key_column_usage where table_schema = '%s' and referenced_table_name is not null" query := fmt.Sprintf(queryForeignKeys, dbh.vtParams.DbName) qr, err := vtConn.ExecuteFetch(query, -1, false) @@ -254,16 +223,11 @@ func (dbh *DBHelper) getForeignKeys() (ForeignKeys, error) { } for _, row := range qr.Rows { tableName := row[0].ToString() - columnName := row[1].ToString() - constraintName := row[2].ToString() - referencedTableName := row[3].ToString() - referencedColumnName := row[4].ToString() fk := &ForeignKey{ - TableName: tableName, - ColumnName: columnName, - ConstraintName: constraintName, - ReferencedTableName: referencedTableName, - ReferencedColumnName: referencedColumnName, + ColumnName: row[1].ToString(), + ConstraintName: row[2].ToString(), + ReferencedTableName: row[3].ToString(), + ReferencedColumnName: row[4].ToString(), } fks[tableName] = append(fks[tableName], fk) } diff --git a/go/testdata/sakila-dbinfo.json b/go/testdata/sakila-dbinfo.json index decc45c..e7aae7f 100644 --- a/go/testdata/sakila-dbinfo.json +++ b/go/testdata/sakila-dbinfo.json @@ -1,69 +1,49 @@ { "fileType": "dbinfo", - "globalVariables": { - "binlog_format": "ROW", - "binlog_row_image": "FULL", - "log_bin": "ON" - }, "tables": [ { - "name": "inventory", - "rows": 4581, + "name": "actor", + "rows": 200, "columns": [ { - "name": "inventory_id", - "type": "mediumint", - "keyType": "PRI", - "isNullable": false, + "name": "actor_id", + "type": "smallint", + "keyType": "pri", "extra": "auto_increment" }, { - "name": "film_id", - "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "first_name", + "type": "varchar" }, { - "name": "store_id", - "type": "tinyint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "last_name", + "type": "varchar", + "keyType": "mul" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" } - ] - }, - { - "name": "language", - "rows": 6, - "columns": [ - { - "name": "language_id", - "type": "tinyint", - "keyType": "PRI", - "isNullable": false, - "extra": "auto_increment" - }, - { - "name": "name", - "type": "char", - "keyType": "", - "isNullable": false, - "extra": "" - }, - { - "name": "last_update", - "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + ], + "primaryKey": { + "columns": [ + "actor_id" + ] + }, + "indexes": [ + { + "Name": "idx_actor_last_name", + "columns": [ + "last_name" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "actor_id" + ] } ] }, @@ -73,120 +53,192 @@ "columns": [ { "name": "actor_id", - "type": "smallint", - "keyType": "", - "isNullable": false, - "extra": "" + "type": "smallint" }, { "name": "first_name", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "type": "varchar" }, { "name": "last_name", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "type": "varchar" }, { "name": "film_info", "type": "text", - "keyType": "", - "isNullable": true, - "extra": "" + "isNullable": true } ] }, { - "name": "staff_list", - "rows": 0, + "name": "address", + "rows": 603, "columns": [ { - "name": "ID", - "type": "tinyint", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "address_id", + "type": "smallint", + "keyType": "pri", + "extra": "auto_increment" }, { - "name": "name", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "address", + "type": "varchar" }, { - "name": "address", + "name": "address2", "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "isNullable": true }, { - "name": "zip code", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "district", + "type": "varchar" }, { - "name": "phone", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "city_id", + "type": "smallint", + "keyType": "mul" }, { - "name": "city", + "name": "postal_code", "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "isNullable": true }, { - "name": "country", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "phone", + "type": "varchar" }, { - "name": "SID", + "name": "location", + "type": "geometry", + "keyType": "mul" + }, + { + "name": "last_update", + "type": "timestamp", + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "address_id" + ] + }, + "indexes": [ + { + "Name": "PRIMARY", + "columns": [ + "address_id" + ] + }, + { + "Name": "idx_fk_city_id", + "columns": [ + "city_id" + ], + "nonUnique": true + }, + { + "Name": "idx_location", + "columns": [ + "location" + ], + "nonUnique": true + } + ], + "foreignKeys": [ + { + "columnName": "city_id", + "constraintName": "fk_address_city", + "referencedTableName": "city", + "referencedColumnName": "city_id" + } + ] + }, + { + "name": "category", + "rows": 16, + "columns": [ + { + "name": "category_id", "type": "tinyint", - "keyType": "", - "isNullable": false, - "extra": "" + "keyType": "pri", + "extra": "auto_increment" + }, + { + "name": "name", + "type": "varchar" + }, + { + "name": "last_update", + "type": "timestamp", + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "category_id" + ] + }, + "indexes": [ + { + "Name": "PRIMARY", + "columns": [ + "category_id" + ] } ] }, { - "name": "film_text", - "rows": 1000, + "name": "city", + "rows": 600, "columns": [ { - "name": "film_id", + "name": "city_id", "type": "smallint", - "keyType": "PRI", - "isNullable": false, - "extra": "" + "keyType": "pri", + "extra": "auto_increment" }, { - "name": "title", - "type": "varchar", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "city", + "type": "varchar" }, { - "name": "description", - "type": "text", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "country_id", + "type": "smallint", + "keyType": "mul" + }, + { + "name": "last_update", + "type": "timestamp", + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "city_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_country_id", + "columns": [ + "country_id" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "city_id" + ] + } + ], + "foreignKeys": [ + { + "columnName": "country_id", + "constraintName": "fk_city_country", + "referencedTableName": "country", + "referencedColumnName": "country_id" } ] }, @@ -197,332 +249,290 @@ { "name": "country_id", "type": "smallint", - "keyType": "PRI", - "isNullable": false, + "keyType": "pri", "extra": "auto_increment" }, { "name": "country", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "type": "varchar" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "country_id" + ] + }, + "indexes": [ + { + "Name": "PRIMARY", + "columns": [ + "country_id" + ] } ] }, { - "name": "nicer_but_slower_film_list", - "rows": 0, + "name": "customer", + "rows": 599, "columns": [ { - "name": "FID", + "name": "customer_id", "type": "smallint", - "keyType": "", - "isNullable": false, - "extra": "" + "keyType": "pri", + "extra": "auto_increment" }, { - "name": "title", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "store_id", + "type": "tinyint", + "keyType": "mul" }, { - "name": "description", - "type": "text", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "first_name", + "type": "varchar" }, { - "name": "category", + "name": "last_name", "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "keyType": "mul" }, { - "name": "price", - "type": "decimal", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "email", + "type": "varchar", + "isNullable": true }, { - "name": "length", + "name": "address_id", "type": "smallint", - "keyType": "", - "isNullable": true, - "extra": "" + "keyType": "mul" }, { - "name": "rating", - "type": "enum", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "active", + "type": "tinyint" }, { - "name": "actors", - "type": "text", - "keyType": "", + "name": "create_date", + "type": "datetime" + }, + { + "name": "last_update", + "type": "timestamp", "isNullable": true, - "extra": "" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "customer_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_store_id", + "columns": [ + "store_id" + ], + "nonUnique": true + }, + { + "Name": "idx_last_name", + "columns": [ + "last_name" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "customer_id" + ] + }, + { + "Name": "idx_fk_address_id", + "columns": [ + "address_id" + ], + "nonUnique": true + } + ], + "foreignKeys": [ + { + "columnName": "address_id", + "constraintName": "fk_customer_address", + "referencedTableName": "address", + "referencedColumnName": "address_id" + }, + { + "columnName": "store_id", + "constraintName": "fk_customer_store", + "referencedTableName": "store", + "referencedColumnName": "store_id" } ] }, { - "name": "film_list", + "name": "customer_list", "rows": 0, "columns": [ { - "name": "FID", - "type": "smallint", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "ID", + "type": "smallint" }, { - "name": "title", + "name": "name", "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "isNullable": true }, { - "name": "description", - "type": "text", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "address", + "type": "varchar" }, { - "name": "category", + "name": "zip code", "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "isNullable": true }, { - "name": "price", - "type": "decimal", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "phone", + "type": "varchar" }, { - "name": "length", - "type": "smallint", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "city", + "type": "varchar" }, { - "name": "rating", - "type": "enum", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "country", + "type": "varchar" }, { - "name": "actors", - "type": "text", - "keyType": "", - "isNullable": true, - "extra": "" - } - ] - }, - { - "name": "sales_by_film_category", - "rows": 0, - "columns": [ - { - "name": "category", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "notes", + "type": "varchar" }, { - "name": "total_sales", - "type": "decimal", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "SID", + "type": "tinyint" } ] }, { - "name": "payment", - "rows": 16086, + "name": "film", + "rows": 1000, "columns": [ { - "name": "payment_id", + "name": "film_id", "type": "smallint", - "keyType": "PRI", - "isNullable": false, + "keyType": "pri", "extra": "auto_increment" }, { - "name": "customer_id", - "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" - }, - { - "name": "staff_id", - "type": "tinyint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "title", + "type": "varchar", + "keyType": "mul" }, { - "name": "rental_id", - "type": "int", - "keyType": "MUL", - "isNullable": true, - "extra": "" + "name": "description", + "type": "text", + "isNullable": true }, { - "name": "amount", - "type": "decimal", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "release_year", + "type": "year", + "isNullable": true }, { - "name": "payment_date", - "type": "datetime", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "language_id", + "type": "tinyint", + "keyType": "mul" }, { - "name": "last_update", - "type": "timestamp", - "keyType": "", - "isNullable": true, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" - } - ] - }, - { - "name": "staff", - "rows": 2, - "columns": [ - { - "name": "staff_id", + "name": "original_language_id", "type": "tinyint", - "keyType": "PRI", - "isNullable": false, - "extra": "auto_increment" + "keyType": "mul", + "isNullable": true }, { - "name": "first_name", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "rental_duration", + "type": "tinyint" }, { - "name": "last_name", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "rental_rate", + "type": "decimal" }, { - "name": "address_id", + "name": "length", "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" - }, - { - "name": "picture", - "type": "blob", - "keyType": "", - "isNullable": true, - "extra": "" + "isNullable": true }, { - "name": "email", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" - }, - { - "name": "store_id", - "type": "tinyint", - "keyType": "MUL", - "isNullable": false, - "extra": "" - }, - { - "name": "active", - "type": "tinyint", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "replacement_cost", + "type": "decimal" }, { - "name": "username", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "rating", + "type": "enum", + "isNullable": true }, { - "name": "password", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "special_features", + "type": "set", + "isNullable": true }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" } - ] - }, - { - "name": "film_category", - "rows": 1000, - "columns": [ - { - "name": "film_id", - "type": "smallint", - "keyType": "PRI", - "isNullable": false, - "extra": "" - }, + ], + "primaryKey": { + "columns": [ + "film_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_language_id", + "columns": [ + "language_id" + ], + "nonUnique": true + }, + { + "Name": "idx_fk_original_language_id", + "columns": [ + "original_language_id" + ], + "nonUnique": true + }, + { + "Name": "idx_title", + "columns": [ + "title" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "film_id" + ] + } + ], + "foreignKeys": [ { - "name": "category_id", - "type": "tinyint", - "keyType": "PRI", - "isNullable": false, - "extra": "" + "columnName": "language_id", + "constraintName": "fk_film_language", + "referencedTableName": "language", + "referencedColumnName": "language_id" }, { - "name": "last_update", - "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "columnName": "original_language_id", + "constraintName": "fk_film_language_original", + "referencedTableName": "language", + "referencedColumnName": "language_id" } ] }, @@ -533,283 +543,437 @@ { "name": "actor_id", "type": "smallint", - "keyType": "PRI", - "isNullable": false, - "extra": "" + "keyType": "pri" }, { "name": "film_id", "type": "smallint", - "keyType": "PRI", - "isNullable": false, - "extra": "" + "keyType": "pri" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "actor_id", + "film_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_film_id", + "columns": [ + "film_id" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "actor_id", + "film_id" + ] + } + ], + "foreignKeys": [ + { + "columnName": "actor_id", + "constraintName": "fk_film_actor_actor", + "referencedTableName": "actor", + "referencedColumnName": "actor_id" + }, + { + "columnName": "film_id", + "constraintName": "fk_film_actor_film", + "referencedTableName": "film", + "referencedColumnName": "film_id" } ] }, { - "name": "city", - "rows": 600, + "name": "film_category", + "rows": 1000, "columns": [ { - "name": "city_id", + "name": "film_id", "type": "smallint", - "keyType": "PRI", - "isNullable": false, - "extra": "auto_increment" + "keyType": "pri" }, { - "name": "city", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" - }, - { - "name": "country_id", - "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "category_id", + "type": "tinyint", + "keyType": "pri" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "film_id", + "category_id" + ] + }, + "indexes": [ + { + "Name": "fk_film_category_category", + "columns": [ + "category_id" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "film_id", + "category_id" + ] + } + ], + "foreignKeys": [ + { + "columnName": "category_id", + "constraintName": "fk_film_category_category", + "referencedTableName": "category", + "referencedColumnName": "category_id" + }, + { + "columnName": "film_id", + "constraintName": "fk_film_category_film", + "referencedTableName": "film", + "referencedColumnName": "film_id" } ] }, { - "name": "customer_list", + "name": "film_list", "rows": 0, "columns": [ { - "name": "ID", - "type": "smallint", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "FID", + "type": "smallint" }, { - "name": "name", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "title", + "type": "varchar" }, { - "name": "address", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "description", + "type": "text", + "isNullable": true }, { - "name": "zip code", + "name": "category", "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "isNullable": true }, { - "name": "phone", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "price", + "type": "decimal" }, { - "name": "city", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "length", + "type": "smallint", + "isNullable": true }, { - "name": "country", + "name": "rating", + "type": "enum", + "isNullable": true + }, + { + "name": "actors", + "type": "text", + "isNullable": true + } + ] + }, + { + "name": "film_text", + "rows": 1000, + "columns": [ + { + "name": "film_id", + "type": "smallint", + "keyType": "pri" + }, + { + "name": "title", "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "keyType": "mul" + }, + { + "name": "description", + "type": "text", + "isNullable": true + } + ], + "primaryKey": { + "columns": [ + "film_id" + ] + }, + "indexes": [ + { + "Name": "PRIMARY", + "columns": [ + "film_id" + ] + }, + { + "Name": "idx_title_description", + "columns": [ + "title", + "description" + ], + "nonUnique": true + } + ] + }, + { + "name": "inventory", + "rows": 4581, + "columns": [ + { + "name": "inventory_id", + "type": "mediumint", + "keyType": "pri", + "extra": "auto_increment" }, { - "name": "notes", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "film_id", + "type": "smallint", + "keyType": "mul" }, { - "name": "SID", + "name": "store_id", "type": "tinyint", - "keyType": "", - "isNullable": false, - "extra": "" - } - ] - }, - { - "name": "sales_by_store", - "rows": 0, - "columns": [ - { - "name": "store", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "keyType": "mul" }, { - "name": "manager", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "last_update", + "type": "timestamp", + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "inventory_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_film_id", + "columns": [ + "film_id" + ], + "nonUnique": true + }, + { + "Name": "idx_store_id_film_id", + "columns": [ + "store_id", + "film_id" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "inventory_id" + ] + } + ], + "foreignKeys": [ + { + "columnName": "film_id", + "constraintName": "fk_inventory_film", + "referencedTableName": "film", + "referencedColumnName": "film_id" }, { - "name": "total_sales", - "type": "decimal", - "keyType": "", - "isNullable": true, - "extra": "" + "columnName": "store_id", + "constraintName": "fk_inventory_store", + "referencedTableName": "store", + "referencedColumnName": "store_id" } ] }, { - "name": "category", - "rows": 16, + "name": "language", + "rows": 6, "columns": [ { - "name": "category_id", + "name": "language_id", "type": "tinyint", - "keyType": "PRI", - "isNullable": false, + "keyType": "pri", "extra": "auto_increment" }, { "name": "name", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "type": "char" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "language_id" + ] + }, + "indexes": [ + { + "Name": "PRIMARY", + "columns": [ + "language_id" + ] } ] }, { - "name": "customer", - "rows": 599, + "name": "nicer_but_slower_film_list", + "rows": 0, "columns": [ { - "name": "customer_id", - "type": "smallint", - "keyType": "PRI", - "isNullable": false, - "extra": "auto_increment" + "name": "FID", + "type": "smallint" }, { - "name": "store_id", - "type": "tinyint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "title", + "type": "varchar" }, { - "name": "first_name", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "description", + "type": "text", + "isNullable": true }, { - "name": "last_name", + "name": "category", "type": "varchar", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "isNullable": true }, { - "name": "email", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "price", + "type": "decimal" }, { - "name": "address_id", + "name": "length", "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" - }, - { - "name": "active", - "type": "tinyint", - "keyType": "", - "isNullable": false, - "extra": "" + "isNullable": true }, { - "name": "create_date", - "type": "datetime", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "rating", + "type": "enum", + "isNullable": true }, { - "name": "last_update", - "type": "timestamp", - "keyType": "", - "isNullable": true, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "name": "actors", + "type": "text", + "isNullable": true } ] }, { - "name": "actor", - "rows": 200, + "name": "payment", + "rows": 16086, "columns": [ { - "name": "actor_id", + "name": "payment_id", "type": "smallint", - "keyType": "PRI", - "isNullable": false, + "keyType": "pri", "extra": "auto_increment" }, { - "name": "first_name", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "customer_id", + "type": "smallint", + "keyType": "mul" }, { - "name": "last_name", - "type": "varchar", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "staff_id", + "type": "tinyint", + "keyType": "mul" + }, + { + "name": "rental_id", + "type": "int", + "keyType": "mul", + "isNullable": true + }, + { + "name": "amount", + "type": "decimal" + }, + { + "name": "payment_date", + "type": "datetime" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "isNullable": true, + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "payment_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_staff_id", + "columns": [ + "staff_id" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "payment_id" + ] + }, + { + "Name": "fk_payment_rental", + "columns": [ + "rental_id" + ], + "nonUnique": true + }, + { + "Name": "idx_fk_customer_id", + "columns": [ + "customer_id" + ], + "nonUnique": true + } + ], + "foreignKeys": [ + { + "columnName": "customer_id", + "constraintName": "fk_payment_customer", + "referencedTableName": "customer", + "referencedColumnName": "customer_id" + }, + { + "columnName": "rental_id", + "constraintName": "fk_payment_rental", + "referencedTableName": "rental", + "referencedColumnName": "rental_id" + }, + { + "columnName": "staff_id", + "constraintName": "fk_payment_staff", + "referencedTableName": "staff", + "referencedColumnName": "staff_id" } ] }, @@ -820,253 +984,350 @@ { "name": "rental_id", "type": "int", - "keyType": "PRI", - "isNullable": false, + "keyType": "pri", "extra": "auto_increment" }, { "name": "rental_date", "type": "datetime", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "keyType": "mul" }, { "name": "inventory_id", "type": "mediumint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "keyType": "mul" }, { "name": "customer_id", "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "keyType": "mul" }, { "name": "return_date", "type": "datetime", - "keyType": "", - "isNullable": true, - "extra": "" + "isNullable": true }, { "name": "staff_id", "type": "tinyint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "keyType": "mul" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "rental_id" + ] + }, + "indexes": [ + { + "Name": "rental_date", + "columns": [ + "rental_date", + "inventory_id", + "customer_id" + ] + }, + { + "Name": "idx_fk_customer_id", + "columns": [ + "customer_id" + ], + "nonUnique": true + }, + { + "Name": "idx_fk_inventory_id", + "columns": [ + "inventory_id" + ], + "nonUnique": true + }, + { + "Name": "idx_fk_staff_id", + "columns": [ + "staff_id" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "rental_id" + ] + } + ], + "foreignKeys": [ + { + "columnName": "customer_id", + "constraintName": "fk_rental_customer", + "referencedTableName": "customer", + "referencedColumnName": "customer_id" + }, + { + "columnName": "inventory_id", + "constraintName": "fk_rental_inventory", + "referencedTableName": "inventory", + "referencedColumnName": "inventory_id" + }, + { + "columnName": "staff_id", + "constraintName": "fk_rental_staff", + "referencedTableName": "staff", + "referencedColumnName": "staff_id" } ] }, { - "name": "store", - "rows": 2, + "name": "sales_by_film_category", + "rows": 0, "columns": [ { - "name": "store_id", - "type": "tinyint", - "keyType": "PRI", - "isNullable": false, - "extra": "auto_increment" + "name": "category", + "type": "varchar" }, { - "name": "manager_staff_id", - "type": "tinyint", - "keyType": "UNI", - "isNullable": false, - "extra": "" + "name": "total_sales", + "type": "decimal", + "isNullable": true + } + ] + }, + { + "name": "sales_by_store", + "rows": 0, + "columns": [ + { + "name": "store", + "type": "varchar", + "isNullable": true }, { - "name": "address_id", - "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "manager", + "type": "varchar", + "isNullable": true }, { - "name": "last_update", - "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "name": "total_sales", + "type": "decimal", + "isNullable": true } ] }, { - "name": "address", - "rows": 603, + "name": "staff", + "rows": 2, "columns": [ { - "name": "address_id", - "type": "smallint", - "keyType": "PRI", - "isNullable": false, + "name": "staff_id", + "type": "tinyint", + "keyType": "pri", "extra": "auto_increment" }, { - "name": "address", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "first_name", + "type": "varchar" }, { - "name": "address2", - "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "last_name", + "type": "varchar" }, { - "name": "district", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "address_id", + "type": "smallint", + "keyType": "mul" }, { - "name": "city_id", - "type": "smallint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "picture", + "type": "blob", + "isNullable": true }, { - "name": "postal_code", + "name": "email", "type": "varchar", - "keyType": "", - "isNullable": true, - "extra": "" + "isNullable": true }, { - "name": "phone", - "type": "varchar", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "store_id", + "type": "tinyint", + "keyType": "mul" }, { - "name": "location", - "type": "geometry", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "active", + "type": "tinyint" + }, + { + "name": "username", + "type": "varchar" + }, + { + "name": "password", + "type": "varchar", + "isNullable": true }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "staff_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_address_id", + "columns": [ + "address_id" + ], + "nonUnique": true + }, + { + "Name": "idx_fk_store_id", + "columns": [ + "store_id" + ], + "nonUnique": true + }, + { + "Name": "PRIMARY", + "columns": [ + "staff_id" + ] + } + ], + "foreignKeys": [ + { + "columnName": "address_id", + "constraintName": "fk_staff_address", + "referencedTableName": "address", + "referencedColumnName": "address_id" + }, + { + "columnName": "store_id", + "constraintName": "fk_staff_store", + "referencedTableName": "store", + "referencedColumnName": "store_id" } ] }, { - "name": "film", - "rows": 1000, + "name": "staff_list", + "rows": 0, "columns": [ { - "name": "film_id", - "type": "smallint", - "keyType": "PRI", - "isNullable": false, - "extra": "auto_increment" + "name": "ID", + "type": "tinyint" }, { - "name": "title", + "name": "name", "type": "varchar", - "keyType": "MUL", - "isNullable": false, - "extra": "" - }, - { - "name": "description", - "type": "text", - "keyType": "", - "isNullable": true, - "extra": "" + "isNullable": true }, { - "name": "release_year", - "type": "year", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "address", + "type": "varchar" }, { - "name": "language_id", - "type": "tinyint", - "keyType": "MUL", - "isNullable": false, - "extra": "" + "name": "zip code", + "type": "varchar", + "isNullable": true }, { - "name": "original_language_id", - "type": "tinyint", - "keyType": "MUL", - "isNullable": true, - "extra": "" + "name": "phone", + "type": "varchar" }, { - "name": "rental_duration", - "type": "tinyint", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "city", + "type": "varchar" }, { - "name": "rental_rate", - "type": "decimal", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "country", + "type": "varchar" }, { - "name": "length", - "type": "smallint", - "keyType": "", - "isNullable": true, - "extra": "" - }, + "name": "SID", + "type": "tinyint" + } + ] + }, + { + "name": "store", + "rows": 2, + "columns": [ { - "name": "replacement_cost", - "type": "decimal", - "keyType": "", - "isNullable": false, - "extra": "" + "name": "store_id", + "type": "tinyint", + "keyType": "pri", + "extra": "auto_increment" }, { - "name": "rating", - "type": "enum", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "manager_staff_id", + "type": "tinyint", + "keyType": "uni" }, { - "name": "special_features", - "type": "set", - "keyType": "", - "isNullable": true, - "extra": "" + "name": "address_id", + "type": "smallint", + "keyType": "mul" }, { "name": "last_update", "type": "timestamp", - "keyType": "", - "isNullable": false, - "extra": "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + "extra": "default_generated on update current_timestamp" + } + ], + "primaryKey": { + "columns": [ + "store_id" + ] + }, + "indexes": [ + { + "Name": "idx_fk_address_id", + "columns": [ + "address_id" + ], + "nonUnique": true + }, + { + "Name": "idx_unique_manager", + "columns": [ + "manager_staff_id" + ] + }, + { + "Name": "PRIMARY", + "columns": [ + "store_id" + ] + } + ], + "foreignKeys": [ + { + "columnName": "address_id", + "constraintName": "fk_store_address", + "referencedTableName": "address", + "referencedColumnName": "address_id" + }, + { + "columnName": "manager_staff_id", + "constraintName": "fk_store_staff", + "referencedTableName": "staff", + "referencedColumnName": "staff_id" } ] } - ] + ], + "globalVariables": { + "binlog_format": "ROW", + "binlog_row_image": "FULL", + "gtid_mode": "OFF", + "log_bin": "ON" + } } \ No newline at end of file