<commit_before>package dbng

import (
	"code.cloudfoundry.org/lager"
	sq "github.com/Masterminds/squirrel"
	"github.com/concourse/atc"
	"github.com/concourse/atc/db/lock"
)

//go:generate counterfeiter . ResourceConfigFactory

type ResourceConfigFactory interface {
	FindOrCreateResourceConfigForBuild(
		logger lager.Logger,
		buildID int,
		resourceType string,
		source atc.Source,
		pipelineID int,
		resourceTypes atc.ResourceTypes,
	) (*UsedResourceConfig, error)

	FindOrCreateResourceConfigForResource(
		logger lager.Logger,
		resourceID int,
		resourceType string,
		source atc.Source,
		pipelineID int,
		resourceTypes atc.ResourceTypes,
	) (*UsedResourceConfig, error)

	FindOrCreateResourceConfigForResourceType(
		logger lager.Logger,
		resourceTypeName string,
		source atc.Source,
		pipelineID int,
		resourceTypes atc.ResourceTypes,
	) (*UsedResourceConfig, error)

	CleanConfigUsesForFinishedBuilds() error
	CleanConfigUsesForInactiveResourceTypes() error
	CleanConfigUsesForInactiveResources() error
	CleanUselessConfigs() error
}

type resourceConfigFactory struct {
	conn        Conn
	lockFactory lock.LockFactory
}

func NewResourceConfigFactory(conn Conn, lockFactory lock.LockFactory) ResourceConfigFactory {
	return &resourceConfigFactory{
		conn:        conn,
		lockFactory: lockFactory,
	}
}

func (f *resourceConfigFactory) FindOrCreateResourceConfigForBuild(
	logger lager.Logger,
	buildID int,
	resourceType string,
	source atc.Source,
	pipelineID int,
	resourceTypes atc.ResourceTypes,
) (*UsedResourceConfig, error) {
	tx, err := f.conn.Begin()
	if err != nil {
		return nil, err
	}

	defer tx.Rollback()

	resourceConfig, err := constructResourceConfig(tx, resourceType, source, resourceTypes, pipelineID)
	if err != nil {
		return nil, err
	}

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

	var usedResourceConfig *UsedResourceConfig

	err = safeFindOrCreate(f.conn, func(tx Tx) error {
		var err error

		usedResourceConfig, err = resourceConfig.FindOrCreateForBuild(logger, tx, f.lockFactory, buildID)
		if err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return usedResourceConfig, nil
}

func (f *resourceConfigFactory) FindOrCreateResourceConfigForResource(
	logger lager.Logger,
	resourceID int,
	resourceType string,
	source atc.Source,
	pipelineID int,
	resourceTypes atc.ResourceTypes,
) (*UsedResourceConfig, error) {
	return findOrCreateResourceConfigForResource(
		f.conn,
		f.lockFactory,
		logger,
		resourceID,
		resourceType,
		source,
		pipelineID,
		resourceTypes,
	)
}

func (f *resourceConfigFactory) FindOrCreateResourceConfigForResourceType(
	logger lager.Logger,
	resourceTypeName string,
	source atc.Source,
	pipelineID int,
	resourceTypes atc.ResourceTypes,
) (*UsedResourceConfig, error) {
	resourceType, found := resourceTypes.Lookup(resourceTypeName)
	if !found {
		return nil, ErrResourceTypeNotFound{resourceTypeName}
	}

	tx, err := f.conn.Begin()
	if err != nil {
		return nil, err
	}

	defer tx.Rollback()

	resourceConfig, err := constructResourceConfig(tx, resourceTypeName, source, resourceTypes, pipelineID)
	if err != nil {
		return nil, err
	}

	rt := ResourceType{
		ResourceType: resourceType,
		PipelineID:   pipelineID,
	}

	usedResourceType, found, err := rt.Find(tx)
	if err != nil {
		return nil, err
	}

	if !found {
		return nil, ErrResourceTypeNotFound{resourceTypeName}
	}

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

	var usedResourceConfig *UsedResourceConfig

	err = safeFindOrCreate(f.conn, func(tx Tx) error {
		var err error
		usedResourceConfig, err = resourceConfig.FindOrCreateForResourceType(logger, tx, f.lockFactory, usedResourceType)
		if err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return usedResourceConfig, nil
}

func constructResourceConfig(
	tx Tx,
	resourceType string,
	source atc.Source,
	resourceTypes []atc.ResourceType,
	pipelineID int,
) (ResourceConfig, error) {
	resourceConfig := ResourceConfig{
		Source: source,
	}

	resourceTypesList := resourceTypesList(resourceType, resourceTypes, []atc.ResourceType{})
	if len(resourceTypesList) == 0 {
		resourceConfig.CreatedByBaseResourceType = &BaseResourceType{
			Name: resourceType,
		}
	} else {
		lastResourceType := resourceTypesList[len(resourceTypesList)-1]
		urt, found, err := ResourceType{
			ResourceType: lastResourceType,
			PipelineID:   pipelineID,
		}.Find(tx)
		if err != nil {
			return ResourceConfig{}, err
		}
		if !found {
			return ResourceConfig{}, ErrResourceTypeNotFound{lastResourceType.Name}
		}

		parentResourceCache := &ResourceCache{
			ResourceConfig: ResourceConfig{
				CreatedByBaseResourceType: &BaseResourceType{
					Name: lastResourceType.Type,
				},
				Source: lastResourceType.Source,
			},
			Version: urt.Version,
		}

		for i := len(resourceTypesList) - 2; i >= 0; i-- {
			urt, found, err := ResourceType{
				ResourceType: resourceTypesList[i],
				PipelineID:   pipelineID,
			}.Find(tx)
			if err != nil {
				return ResourceConfig{}, err
			}
			if !found {
				return ResourceConfig{}, ErrResourceTypeNotFound{resourceTypesList[i].Name}
			}

			parentResourceCache = &ResourceCache{
				ResourceConfig: ResourceConfig{
					CreatedByResourceCache: parentResourceCache,
					Source:                 resourceTypesList[i].Source,
				},
				Version: urt.Version,
			}
		}

		resourceConfig.CreatedByResourceCache = parentResourceCache
	}

	return resourceConfig, nil
}

func (f *resourceConfigFactory) CleanConfigUsesForFinishedBuilds() error {
	tx, err := f.conn.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, err = psql.Delete("resource_config_uses rcu USING builds b").
		Where(sq.Expr("rcu.build_id = b.id")).
		Where(sq.Expr("rcu.build_id IN (SELECT id FROM builds WHERE interceptible=false)")).
		RunWith(tx).
		Exec()

	if err != nil {
		return err
	}

	return tx.Commit()
}

func (f *resourceConfigFactory) CleanConfigUsesForInactiveResourceTypes() error {
	tx, err := f.conn.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, err = psql.Delete("resource_config_uses rcu USING resource_types t").
		Where(sq.And{
			sq.Expr("rcu.resource_type_id = t.id"),
			sq.Eq{<commit_msg>remove unnecessary subquery<commit_after>package dbng

import (
	"code.cloudfoundry.org/lager"
	sq "github.com/Masterminds/squirrel"
	"github.com/concourse/atc"
	"github.com/concourse/atc/db/lock"
)

//go:generate counterfeiter . ResourceConfigFactory

type ResourceConfigFactory interface {
	FindOrCreateResourceConfigForBuild(
		logger lager.Logger,
		buildID int,
		resourceType string,
		source atc.Source,
		pipelineID int,
		resourceTypes atc.ResourceTypes,
	) (*UsedResourceConfig, error)

	FindOrCreateResourceConfigForResource(
		logger lager.Logger,
		resourceID int,
		resourceType string,
		source atc.Source,
		pipelineID int,
		resourceTypes atc.ResourceTypes,
	) (*UsedResourceConfig, error)

	FindOrCreateResourceConfigForResourceType(
		logger lager.Logger,
		resourceTypeName string,
		source atc.Source,
		pipelineID int,
		resourceTypes atc.ResourceTypes,
	) (*UsedResourceConfig, error)

	CleanConfigUsesForFinishedBuilds() error
	CleanConfigUsesForInactiveResourceTypes() error
	CleanConfigUsesForInactiveResources() error
	CleanUselessConfigs() error
}

type resourceConfigFactory struct {
	conn        Conn
	lockFactory lock.LockFactory
}

func NewResourceConfigFactory(conn Conn, lockFactory lock.LockFactory) ResourceConfigFactory {
	return &resourceConfigFactory{
		conn:        conn,
		lockFactory: lockFactory,
	}
}

func (f *resourceConfigFactory) FindOrCreateResourceConfigForBuild(
	logger lager.Logger,
	buildID int,
	resourceType string,
	source atc.Source,
	pipelineID int,
	resourceTypes atc.ResourceTypes,
) (*UsedResourceConfig, error) {
	tx, err := f.conn.Begin()
	if err != nil {
		return nil, err
	}

	defer tx.Rollback()

	resourceConfig, err := constructResourceConfig(tx, resourceType, source, resourceTypes, pipelineID)
	if err != nil {
		return nil, err
	}

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

	var usedResourceConfig *UsedResourceConfig

	err = safeFindOrCreate(f.conn, func(tx Tx) error {
		var err error

		usedResourceConfig, err = resourceConfig.FindOrCreateForBuild(logger, tx, f.lockFactory, buildID)
		if err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return usedResourceConfig, nil
}

func (f *resourceConfigFactory) FindOrCreateResourceConfigForResource(
	logger lager.Logger,
	resourceID int,
	resourceType string,
	source atc.Source,
	pipelineID int,
	resourceTypes atc.ResourceTypes,
) (*UsedResourceConfig, error) {
	return findOrCreateResourceConfigForResource(
		f.conn,
		f.lockFactory,
		logger,
		resourceID,
		resourceType,
		source,
		pipelineID,
		resourceTypes,
	)
}

func (f *resourceConfigFactory) FindOrCreateResourceConfigForResourceType(
	logger lager.Logger,
	resourceTypeName string,
	source atc.Source,
	pipelineID int,
	resourceTypes atc.ResourceTypes,
) (*UsedResourceConfig, error) {
	resourceType, found := resourceTypes.Lookup(resourceTypeName)
	if !found {
		return nil, ErrResourceTypeNotFound{resourceTypeName}
	}

	tx, err := f.conn.Begin()
	if err != nil {
		return nil, err
	}

	defer tx.Rollback()

	resourceConfig, err := constructResourceConfig(tx, resourceTypeName, source, resourceTypes, pipelineID)
	if err != nil {
		return nil, err
	}

	rt := ResourceType{
		ResourceType: resourceType,
		PipelineID:   pipelineID,
	}

	usedResourceType, found, err := rt.Find(tx)
	if err != nil {
		return nil, err
	}

	if !found {
		return nil, ErrResourceTypeNotFound{resourceTypeName}
	}

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

	var usedResourceConfig *UsedResourceConfig

	err = safeFindOrCreate(f.conn, func(tx Tx) error {
		var err error
		usedResourceConfig, err = resourceConfig.FindOrCreateForResourceType(logger, tx, f.lockFactory, usedResourceType)
		if err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return usedResourceConfig, nil
}

func constructResourceConfig(
	tx Tx,
	resourceType string,
	source atc.Source,
	resourceTypes []atc.ResourceType,
	pipelineID int,
) (ResourceConfig, error) {
	resourceConfig := ResourceConfig{
		Source: source,
	}

	resourceTypesList := resourceTypesList(resourceType, resourceTypes, []atc.ResourceType{})
	if len(resourceTypesList) == 0 {
		resourceConfig.CreatedByBaseResourceType = &BaseResourceType{
			Name: resourceType,
		}
	} else {
		lastResourceType := resourceTypesList[len(resourceTypesList)-1]
		urt, found, err := ResourceType{
			ResourceType: lastResourceType,
			PipelineID:   pipelineID,
		}.Find(tx)
		if err != nil {
			return ResourceConfig{}, err
		}
		if !found {
			return ResourceConfig{}, ErrResourceTypeNotFound{lastResourceType.Name}
		}

		parentResourceCache := &ResourceCache{
			ResourceConfig: ResourceConfig{
				CreatedByBaseResourceType: &BaseResourceType{
					Name: lastResourceType.Type,
				},
				Source: lastResourceType.Source,
			},
			Version: urt.Version,
		}

		for i := len(resourceTypesList) - 2; i >= 0; i-- {
			urt, found, err := ResourceType{
				ResourceType: resourceTypesList[i],
				PipelineID:   pipelineID,
			}.Find(tx)
			if err != nil {
				return ResourceConfig{}, err
			}
			if !found {
				return ResourceConfig{}, ErrResourceTypeNotFound{resourceTypesList[i].Name}
			}

			parentResourceCache = &ResourceCache{
				ResourceConfig: ResourceConfig{
					CreatedByResourceCache: parentResourceCache,
					Source:                 resourceTypesList[i].Source,
				},
				Version: urt.Version,
			}
		}

		resourceConfig.CreatedByResourceCache = parentResourceCache
	}

	return resourceConfig, nil
}

func (f *resourceConfigFactory) CleanConfigUsesForFinishedBuilds() error {
	tx, err := f.conn.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, err = psql.Delete("resource_config_uses rcu USING builds b").
		Where(sq.Expr("rcu.build_id = b.id")).
		Where(sq.Expr("NOT b.interceptible")).
		RunWith(tx).
		Exec()

	if err != nil {
		return err
	}

	return tx.Commit()
}

func (f *resourceConfigFactory) CleanConfigUsesForInactiveResourceTypes() error {
	tx, err := f.conn.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, err = psql.Delete("resource_config_uses rcu USING resource_types t").
		Where(sq.And{
			sq.Expr("rcu.resource_type_id = t.id"),
			sq.Eq{
{'fraction_non_alphanumeric': 0.09996788180504255, 'fraction_numerical': 0.0006423638991488678, 'mean_word_length': 4.398786302557434, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6936208', 'n_tokens_mistral': 4356, 'n_tokens_neox': 3998, 'n_words': 947}