func()

in sources/spanner/infoschema.go [306:380]


func (isi InfoSchemaImpl) GetForeignKeys(conv *internal.Conv, table common.SchemaAndName) (foreignKeys []schema.ForeignKey, err error) {
	q := `SELECT  k.constraint_name, k.column_name, c.table_name, c.column_name 
			FROM information_schema.key_column_usage AS k 
			JOIN information_schema.constraint_column_usage AS c ON k.constraint_name = c.constraint_name
			JOIN information_schema.table_constraints AS t ON k.constraint_name = t.constraint_name 
			WHERE t.constraint_type='FOREIGN KEY' AND t.table_schema = '' AND t.table_name = @p1
			ORDER BY k.constraint_name, k.ordinal_position;`
	if isi.SpDialect == constants.DIALECT_POSTGRESQL {
		q = `SELECT  k.constraint_name, k.column_name, c.table_name, c.column_name 
				FROM information_schema.key_column_usage AS k 
				JOIN information_schema.constraint_column_usage AS c ON k.constraint_name = c.constraint_name
				JOIN information_schema.table_constraints AS t ON k.constraint_name = t.constraint_name 
				WHERE t.constraint_type='FOREIGN KEY' AND t.table_schema = 'public' AND t.table_name = $1
				ORDER BY k.constraint_name, k.ordinal_position;`
	}
	stmt := spanner.Statement{
		SQL: q,
		Params: map[string]interface{}{
			"p1": table.Name,
		},
	}
	var iter spannerclient.RowIterator
	if isi.SpannerClient != nil {
		iter = isi.SpannerClient.Single().Query(isi.Ctx, stmt)
	} else {
		iter = isi.Client.Single().Query(isi.Ctx, stmt)
	}
	defer iter.Stop()

	var col, refCol, fKeyName, refTable string
	fKeys := make(map[string]common.FkConstraint)
	var keyNames []string
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("couldn't get row while fetching foreign keys: %w", err)
		}
		err = row.Columns(&fKeyName, &col, &refTable, &refCol)
		if err != nil {
			return nil, err
		}
		if _, found := fKeys[fKeyName]; found {
			fk := fKeys[fKeyName]
			fk.Cols = append(fk.Cols, col)
			fk.Refcols = append(fk.Refcols, refCol)
			fKeys[fKeyName] = fk
			continue
		}
		fKeys[fKeyName] = common.FkConstraint{Name: fKeyName, Table: isi.GetTableName(table.Schema, refTable), Refcols: []string{refCol}, Cols: []string{col}}
		keyNames = append(keyNames, fKeyName)
	}
	sort.Strings(keyNames)
	for _, k := range keyNames {
		// The query returns a crypted result for multi-col FKs. Currently for a FK from (a,b,c) -> (x,y,z),
		// the returned rows like (a,x), (a,y), (a,z), (b,x), (b,y), (b,z), (c,x), (c,y), (c,z).
		// Need to reduce it to (a,x), (b,y), (c,z). The logic below does that.
		n := int(math.Sqrt(float64(len(fKeys[k].Cols))))
		cols, refcols := []string{}, []string{}
		for i := 0; i < n; i++ {
			cols = append(cols, fKeys[k].Cols[i*n])
			refcols = append(refcols, fKeys[k].Refcols[i])
		}
		foreignKeys = append(foreignKeys,
			schema.ForeignKey{
				Id:               internal.GenerateForeignkeyId(),
				Name:             fKeys[k].Name,
				ColumnNames:      cols,
				ReferTableName:   fKeys[k].Table,
				ReferColumnNames: refcols})
	}
	return foreignKeys, nil
}