func (tm *TableManager) EnsureStructure(
	ctx context.Context,
	db dbh,
	tbl *tableState,
	columns []utils.Column,
	createTemplates []*sqltemplate.Template,
	addColumnsTemplates []*sqltemplate.Template,
	metricsTable *tableState,
	tagsTable *tableState,
) ([]utils.Column, error) {
	// Sort so that:
	//   * When we create/alter the table the columns are in a sane order (telegraf gives us the fields in random order)
	//   * When we display errors about missing columns, the order is also sane, and consistent
	utils.ColumnList(columns).Sort()

	// rlock, read, runlock, wlock, read, read_db, wlock_db, read_db, write_db, wunlock_db, wunlock

	// rlock
	tbl.RLock()
	// read
	currCols := tbl.columns
	// runlock
	tbl.RUnlock()
	missingCols := diffMissingColumns(currCols, columns)
	if len(missingCols) == 0 {
		return nil, nil
	}

	// wlock
	// We also need to lock the other table as it may be referenced by a template.
	// To prevent deadlock, the metric & tag table must always be locked in the same order: 1) Tag, 2) Metric
	if tbl == tagsTable {
		tagsTable.Lock()
		defer tagsTable.Unlock()

		metricsTable.RLock()
		defer metricsTable.RUnlock()
	} else {
		if tagsTable != nil {
			tagsTable.RLock()
			defer tagsTable.RUnlock()
		}

		metricsTable.Lock()
		defer metricsTable.Unlock()
	}

	// read
	currCols = tbl.columns
	missingCols = diffMissingColumns(currCols, columns)
	if len(missingCols) == 0 {
		return nil, nil
	}

	// read_db
	var err error
	if currCols, err = tm.getColumns(ctx, db, tbl.name); err != nil {
		return nil, err
	}
	tbl.columns = currCols
	missingCols = diffMissingColumns(currCols, columns)
	if len(missingCols) == 0 {
		tbl.columns = currCols
		return nil, nil
	}

	if len(currCols) == 0 && len(createTemplates) == 0 {
		// can't create
		return missingCols, nil
	}
	if len(currCols) != 0 && len(addColumnsTemplates) == 0 {
		// can't add
		return missingCols, nil
	}

	// wlock_db
	tx, err := db.Begin(ctx)
	if err != nil {
		return nil, err
	}
	defer tx.Rollback(ctx) //nolint:errcheck
	// It's possible to have multiple telegraf processes, in which we can't ensure they all lock tables in the same
	// order. So to prevent possible deadlocks, we have to have a single lock for all schema modifications.
	if _, err := tx.Exec(ctx, "SELECT pg_advisory_xact_lock($1)", schemaAdvisoryLockID); err != nil {
		return nil, err
	}

	// read_db
	if currCols, err = tm.getColumns(ctx, tx, tbl.name); err != nil {
		return nil, err
	}
	tbl.columns = currCols
	if currCols != nil {
		missingCols = diffMissingColumns(currCols, columns)
		if len(missingCols) == 0 {
			return nil, nil
		}
	}

	// write_db
	var tmpls []*sqltemplate.Template
	if len(currCols) == 0 {
		tmpls = createTemplates
	} else {
		tmpls = addColumnsTemplates
	}
	if err := tm.update(ctx, tx, tbl, tmpls, missingCols, metricsTable, tagsTable); err != nil {
		return nil, err
	}

	if currCols, err = tm.getColumns(ctx, tx, tbl.name); err != nil {
		return nil, err
	}

	if err := tx.Commit(ctx); err != nil {
		return nil, err
	}

	tbl.columns = currCols

	// wunlock_db (deferred)
	// wunlock (deferred)

	return nil, nil
}
// EnsureStructure ensures that the table identified by tableName contains the provided columns.
//
// createTemplates and addColumnTemplates are the templates which are executed in the event of table create or alter
// (respectively).
// metricsTableName and tagsTableName are passed to the templates.
//
// If the table cannot be modified, the returned column list is the columns which are missing from the table.
//nolint:revive
EnsureStructure ensures that the table identified by tableName contains the provided columns.

createTemplates and addColumnTemplates are the templates which are executed in the event of table create or alter
(respectively).
metricsTableName and tagsTableName are passed to the templates.

If the table cannot be modified, the returned column list is the columns which are missing from the table.
nolint:revive
// EnsureStructure ensures that the table identified by tableName contains the provided columns . // // createTemplates and addColumnTemplates are the templates which are executed in the event of table create or alter // ( respectively ) . // metricsTableName and tagsTableName are passed to the templates . // // If the table cannot be modified the returned column list is the columns which are missing from the table . // nolint : revive
func (tm *TableManager) EnsureStructure(
	ctx context.Context,
	db dbh,
	tbl *tableState,
	columns []utils.Column,
	createTemplates []*sqltemplate.Template,
	addColumnsTemplates []*sqltemplate.Template,
	metricsTable *tableState,
	tagsTable *tableState,
) ([]utils.Column, error) {
	utils.ColumnList(columns).Sort()
	tbl.RLock()
	currCols := tbl.columns
	tbl.RUnlock()
	missingCols := diffMissingColumns(currCols, columns)
	if len(missingCols) == 0 {
		return nil, nil
	}
	if tbl == tagsTable {
		tagsTable.Lock()
		defer tagsTable.Unlock()
		metricsTable.RLock()
		defer metricsTable.RUnlock()
	} else {
		if tagsTable != nil {
			tagsTable.RLock()
			defer tagsTable.RUnlock()
		}
		metricsTable.Lock()
		defer metricsTable.Unlock()
	}
	currCols = tbl.columns
	missingCols = diffMissingColumns(currCols, columns)
	if len(missingCols) == 0 {
		return nil, nil
	}
	_db
	var err error
	if currCols, err = tm.getColumns(ctx, db, tbl.name); err != nil {
		return nil, err
	}
	tbl.columns = currCols
	missingCols = diffMissingColumns(currCols, columns)
	if len(missingCols) == 0 {
		tbl.columns = currCols
		return nil, nil
	}
	if len(currCols) == 0 && len(createTemplates) == 0 {
		return missingCols, nil
	}
	if len(currCols) != 0 && len(addColumnsTemplates) == 0 {
		return missingCols, nil
	}
	_db
	tx, err := db.Begin(ctx)
	if err != nil {
		return nil, err
	}
	defer tx.Rollback(ctx) 
	if _, err := tx.Exec(ctx, "SELECT pg_advisory_xact_lock($1)", schemaAdvisoryLockID); err != nil {
		return nil, err
	}
	_db
	if currCols, err = tm.getColumns(ctx, tx, tbl.name); err != nil {
		return nil, err
	}
	tbl.columns = currCols
	if currCols != nil {
		missingCols = diffMissingColumns(currCols, columns)
		if len(missingCols) == 0 {
			return nil, nil
		}
	}
	var tmpls []*sqltemplate.Template
	if len(currCols) == 0 {
		tmpls = createTemplates
	} else {
		tmpls = addColumnsTemplates
	}
	if err := tm.update(ctx, tx, tbl, tmpls, missingCols, metricsTable, tagsTable); err != nil {
		return nil, err
	}
	if currCols, err = tm.getColumns(ctx, tx, tbl.name); err != nil {
		return nil, err
	}
	if err := tx.Commit(ctx); err != nil {
		return nil, err
	}
	tbl.columns = currCols
	return nil, nil
}
func ( tm * TableManager ) EnsureStructure ( ctx context . Context , db dbh , tbl * tableState , columns [ ] utils . Column , createTemplates [ ] * sqltemplate . Template , addColumnsTemplates [ ] * sqltemplate . Template , metricsTable * tableState , tagsTable * tableState , ) ( [ ] utils . Column , error ) { utils . ColumnList ( columns ) . Sort ( ) tbl . RLock ( ) currCols := tbl . columns tbl . RUnlock ( ) missingCols := diffMissingColumns ( currCols , columns ) if len ( missingCols ) == 0 { return nil , nil } if tbl == tagsTable { tagsTable . Lock ( ) defer tagsTable . Unlock ( ) metricsTable . RLock ( ) defer metricsTable . RUnlock ( ) } else { if tagsTable != nil { tagsTable . RLock ( ) defer tagsTable . RUnlock ( ) } metricsTable . Lock ( ) defer metricsTable . Unlock ( ) } currCols = tbl . columns missingCols = diffMissingColumns ( currCols , columns ) if len ( missingCols ) == 0 { return nil , nil } var err error if currCols , err = tm . getColumns ( ctx , db , tbl . name ) ; err != nil { return nil , err } tbl . columns = currCols missingCols = diffMissingColumns ( currCols , columns ) if len ( missingCols ) == 0 { tbl . columns = currCols return nil , nil } if len ( currCols ) == 0 && len ( createTemplates ) == 0 { return missingCols , nil } if len ( currCols ) != 0 && len ( addColumnsTemplates ) == 0 { return missingCols , nil } tx , err := db . Begin ( ctx ) if err != nil { return nil , err } defer tx . Rollback ( ctx ) if _ , err := tx . Exec ( ctx , " " , schemaAdvisoryLockID ) ; err != nil { return nil , err } if currCols , err = tm . getColumns ( ctx , tx , tbl . name ) ; err != nil { return nil , err } tbl . columns = currCols if currCols != nil { missingCols = diffMissingColumns ( currCols , columns ) if len ( missingCols ) == 0 { return nil , nil } } var tmpls [ ] * sqltemplate . Template if len ( currCols ) == 0 { tmpls = createTemplates } else { tmpls = addColumnsTemplates } if err := tm . update ( ctx , tx , tbl , tmpls , missingCols , metricsTable , tagsTable ) ; err != nil { return nil , err } if currCols , err = tm . getColumns ( ctx , tx , tbl . name ) ; err != nil { return nil , err } if err := tx . Commit ( ctx ) ; err != nil { return nil , err } tbl . columns = currCols return nil , nil }
EnsureStructure ensures that the table identified by tableName contains the provided columns.
EnsureStructure ensures that the table identified by tableName contains the provided columns .
// Sort so that: //   * When we create/alter the table the columns are in a sane order (telegraf gives us the fields in random order) //   * When we display errors about missing columns, the order is also sane, and consistent // rlock, read, runlock, wlock, read, read_db, wlock_db, read_db, write_db, wunlock_db, wunlock // rlock // read // runlock // wlock // We also need to lock the other table as it may be referenced by a template. // To prevent deadlock, the metric & tag table must always be locked in the same order: 1) Tag, 2) Metric // read // read_db // can't create // can't add // wlock_db //nolint:errcheck // It's possible to have multiple telegraf processes, in which we can't ensure they all lock tables in the same // order. So to prevent possible deadlocks, we have to have a single lock for all schema modifications. // read_db // write_db // wunlock_db (deferred) // wunlock (deferred)