github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/storage/postsql/read.go (about)

     1  package postsql
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/kyma-project/kyma-environment-broker/common/events"
     9  	"github.com/kyma-project/kyma-environment-broker/common/orchestration"
    10  	"github.com/kyma-project/kyma-environment-broker/internal"
    11  	"github.com/kyma-project/kyma-environment-broker/internal/storage/dberr"
    12  	"github.com/kyma-project/kyma-environment-broker/internal/storage/dbmodel"
    13  	"github.com/kyma-project/kyma-environment-broker/internal/storage/predicate"
    14  	"golang.org/x/exp/slices"
    15  
    16  	"github.com/gocraft/dbr"
    17  	"github.com/pivotal-cf/brokerapi/v8/domain"
    18  )
    19  
    20  type readSession struct {
    21  	session *dbr.Session
    22  }
    23  
    24  func (r readSession) getInstancesJoinedWithOperationStatement() *dbr.SelectStmt {
    25  	join := fmt.Sprintf("%s.instance_id = %s.instance_id", InstancesTableName, OperationTableName)
    26  	stmt := r.session.
    27  		Select("instances.instance_id, instances.runtime_id, instances.global_account_id, instances.subscription_global_account_id, instances.service_id,"+
    28  			" instances.service_plan_id, instances.dashboard_url, instances.provisioning_parameters, instances.created_at,"+
    29  			" instances.updated_at, instances.deleted_at, instances.sub_account_id, instances.service_name, instances.service_plan_name,"+
    30  			" instances.provider_region, instances.provider, operations.state, operations.description, operations.type, operations.created_at AS operation_created_at, operations.data").
    31  		From(InstancesTableName).
    32  		LeftJoin(OperationTableName, join)
    33  	return stmt
    34  }
    35  
    36  func (r readSession) FindAllInstancesJoinedWithOperation(prct ...predicate.Predicate) ([]dbmodel.InstanceWithOperationDTO, dberr.Error) {
    37  	var instances []dbmodel.InstanceWithOperationDTO
    38  
    39  	stmt := r.getInstancesJoinedWithOperationStatement()
    40  	for _, p := range prct {
    41  		p.ApplyToPostgres(stmt)
    42  	}
    43  
    44  	if _, err := stmt.Load(&instances); err != nil {
    45  		return nil, dberr.Internal("Failed to fetch all instances: %s", err)
    46  	}
    47  
    48  	return instances, nil
    49  }
    50  
    51  func (r readSession) GetInstanceByID(instanceID string) (dbmodel.InstanceDTO, dberr.Error) {
    52  	var instance dbmodel.InstanceDTO
    53  
    54  	err := r.session.
    55  		Select("*").
    56  		From(InstancesTableName).
    57  		Where(dbr.Eq("instance_id", instanceID)).
    58  		LoadOne(&instance)
    59  
    60  	if err != nil {
    61  		if err == dbr.ErrNotFound {
    62  			return dbmodel.InstanceDTO{}, dberr.NotFound("Cannot find Instance for instanceID:'%s'", instanceID)
    63  		}
    64  		return dbmodel.InstanceDTO{}, dberr.Internal("Failed to get Instance: %s", err)
    65  	}
    66  
    67  	return instance, nil
    68  }
    69  
    70  func (r readSession) FindAllInstancesForRuntimes(runtimeIdList []string) ([]dbmodel.InstanceDTO, dberr.Error) {
    71  	var instances []dbmodel.InstanceDTO
    72  
    73  	err := r.session.
    74  		Select("*").
    75  		From(InstancesTableName).
    76  		Where("runtime_id IN ?", runtimeIdList).
    77  		LoadOne(&instances)
    78  
    79  	if err != nil {
    80  		if err == dbr.ErrNotFound {
    81  			return []dbmodel.InstanceDTO{}, dberr.NotFound("Cannot find Instances for runtime ID list: '%v'", runtimeIdList)
    82  		}
    83  		return []dbmodel.InstanceDTO{}, dberr.Internal("Failed to get Instances: %s", err)
    84  	}
    85  	return instances, nil
    86  }
    87  
    88  func (r readSession) FindAllInstancesForSubAccounts(subAccountslist []string) ([]dbmodel.InstanceDTO, dberr.Error) {
    89  	var instances []dbmodel.InstanceDTO
    90  
    91  	err := r.session.
    92  		Select("*").
    93  		From(InstancesTableName).
    94  		Where("sub_account_id IN ?", subAccountslist).
    95  		LoadOne(&instances)
    96  
    97  	if err != nil {
    98  		if err == dbr.ErrNotFound {
    99  			return []dbmodel.InstanceDTO{}, nil
   100  		}
   101  		return []dbmodel.InstanceDTO{}, dberr.Internal("Failed to get Instances: %s", err)
   102  	}
   103  	return instances, nil
   104  }
   105  
   106  func (r readSession) GetLastOperation(instanceID string) (dbmodel.OperationDTO, dberr.Error) {
   107  	inst := dbr.Eq("instance_id", instanceID)
   108  	state := dbr.Neq("state", []string{orchestration.Pending, orchestration.Canceled})
   109  	condition := dbr.And(inst, state)
   110  	operation, err := r.getLastOperation(condition)
   111  	if err != nil {
   112  		switch {
   113  		case dberr.IsNotFound(err):
   114  			return dbmodel.OperationDTO{}, dberr.NotFound("for instance ID: %s %s", instanceID, err)
   115  		default:
   116  			return dbmodel.OperationDTO{}, err
   117  		}
   118  	}
   119  	return operation, nil
   120  }
   121  
   122  func (r readSession) GetOperationByInstanceID(instanceId string) (dbmodel.OperationDTO, dberr.Error) {
   123  	condition := dbr.Eq("instance_id", instanceId)
   124  	operation, err := r.getOperation(condition)
   125  	if err != nil {
   126  		switch {
   127  		case dberr.IsNotFound(err):
   128  			return dbmodel.OperationDTO{}, dberr.NotFound("for instance_id: %s %s", instanceId, err)
   129  		default:
   130  			return dbmodel.OperationDTO{}, err
   131  		}
   132  	}
   133  	return operation, nil
   134  }
   135  
   136  func (r readSession) GetOperationByID(opID string) (dbmodel.OperationDTO, dberr.Error) {
   137  	condition := dbr.Eq("id", opID)
   138  	operation, err := r.getOperation(condition)
   139  	if err != nil {
   140  		switch {
   141  		case dberr.IsNotFound(err):
   142  			return dbmodel.OperationDTO{}, dberr.NotFound("for ID: %s %s", opID, err)
   143  		default:
   144  			return dbmodel.OperationDTO{}, err
   145  		}
   146  	}
   147  	return operation, nil
   148  }
   149  
   150  func (r readSession) ListOperations(filter dbmodel.OperationFilter) ([]dbmodel.OperationDTO, int, int, error) {
   151  	var operations []dbmodel.OperationDTO
   152  
   153  	stmt := r.session.Select("o.*").
   154  		From(dbr.I(OperationTableName).As("o")).
   155  		OrderBy("o.created_at")
   156  
   157  	// Add pagination if provided
   158  	if filter.Page > 0 && filter.PageSize > 0 {
   159  		stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize))
   160  	}
   161  
   162  	// Apply filtering if provided
   163  	addOperationFilters(stmt, filter)
   164  
   165  	_, err := stmt.Load(&operations)
   166  
   167  	totalCount, err := r.getOperationCount(filter)
   168  	if err != nil {
   169  		return nil, -1, -1, err
   170  	}
   171  
   172  	return operations,
   173  		len(operations),
   174  		totalCount,
   175  		nil
   176  }
   177  
   178  func (r readSession) GetOrchestrationByID(oID string) (dbmodel.OrchestrationDTO, dberr.Error) {
   179  	condition := dbr.Eq("orchestration_id", oID)
   180  	operation, err := r.getOrchestration(condition)
   181  	if err != nil {
   182  		switch {
   183  		case dberr.IsNotFound(err):
   184  			return dbmodel.OrchestrationDTO{}, dberr.NotFound("for ID: %s %s", oID, err)
   185  		default:
   186  			return dbmodel.OrchestrationDTO{}, err
   187  		}
   188  	}
   189  	return operation, nil
   190  }
   191  
   192  func (r readSession) ListOrchestrations(filter dbmodel.OrchestrationFilter) ([]dbmodel.OrchestrationDTO, int, int, error) {
   193  	var orchestrations []dbmodel.OrchestrationDTO
   194  
   195  	stmt := r.session.Select("*").
   196  		From(OrchestrationTableName).
   197  		OrderBy(CreatedAtField)
   198  
   199  	// Add pagination if provided
   200  	if filter.Page > 0 && filter.PageSize > 0 {
   201  		stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize))
   202  	}
   203  
   204  	// Apply filtering if provided
   205  	addOrchestrationFilters(stmt, filter)
   206  
   207  	_, err := stmt.Load(&orchestrations)
   208  
   209  	totalCount, err := r.getOrchestrationCount(filter)
   210  	if err != nil {
   211  		return nil, -1, -1, err
   212  	}
   213  
   214  	return orchestrations,
   215  		len(orchestrations),
   216  		totalCount,
   217  		nil
   218  }
   219  
   220  func (r readSession) CountNotFinishedOperationsByInstanceID(instanceID string) (int, dberr.Error) {
   221  	stateInProgress := dbr.Eq("state", domain.InProgress)
   222  	statePending := dbr.Eq("state", orchestration.Pending)
   223  	stateCondition := dbr.Or(statePending, stateInProgress)
   224  	instanceIDCondition := dbr.Eq("instance_id", instanceID)
   225  
   226  	var res struct {
   227  		Total int
   228  	}
   229  	err := r.session.Select("count(*) as total").
   230  		From(OperationTableName).
   231  		Where(stateCondition).
   232  		Where(instanceIDCondition).
   233  		LoadOne(&res)
   234  
   235  	if err != nil {
   236  		return 0, dberr.Internal("Failed to count operations: %s", err)
   237  	}
   238  	return res.Total, nil
   239  }
   240  
   241  func (r readSession) GetNotFinishedOperationsByType(operationType internal.OperationType) ([]dbmodel.OperationDTO, dberr.Error) {
   242  	stateInProgress := dbr.Eq("state", domain.InProgress)
   243  	statePending := dbr.Eq("state", orchestration.Pending)
   244  	stateCondition := dbr.Or(statePending, stateInProgress)
   245  	typeCondition := dbr.Eq("type", operationType)
   246  	var operations []dbmodel.OperationDTO
   247  
   248  	_, err := r.session.
   249  		Select("*").
   250  		From(OperationTableName).
   251  		Where(stateCondition).
   252  		Where(typeCondition).
   253  		Load(&operations)
   254  	if err != nil {
   255  		return nil, dberr.Internal("Failed to get operations: %s", err)
   256  	}
   257  	return operations, nil
   258  }
   259  
   260  func (r readSession) GetOperationByTypeAndInstanceID(inID string, opType internal.OperationType) (dbmodel.OperationDTO, dberr.Error) {
   261  	idCondition := dbr.Eq("instance_id", inID)
   262  	typeCondition := dbr.Eq("type", string(opType))
   263  	var operation dbmodel.OperationDTO
   264  
   265  	err := r.session.
   266  		Select("*").
   267  		From(OperationTableName).
   268  		Where(idCondition).
   269  		Where(typeCondition).
   270  		OrderDesc(CreatedAtField).
   271  		LoadOne(&operation)
   272  
   273  	if err != nil {
   274  		if err == dbr.ErrNotFound {
   275  			return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err)
   276  		}
   277  		return dbmodel.OperationDTO{}, dberr.Internal("Failed to get operation: %s", err)
   278  	}
   279  	return operation, nil
   280  }
   281  
   282  func (r readSession) GetOperationsByTypeAndInstanceID(inID string, opType internal.OperationType) ([]dbmodel.OperationDTO, dberr.Error) {
   283  	idCondition := dbr.Eq("instance_id", inID)
   284  	typeCondition := dbr.Eq("type", string(opType))
   285  	var operations []dbmodel.OperationDTO
   286  
   287  	_, err := r.session.
   288  		Select("*").
   289  		From(OperationTableName).
   290  		Where(idCondition).
   291  		Where(typeCondition).
   292  		OrderDesc(CreatedAtField).
   293  		Load(&operations)
   294  
   295  	if err != nil {
   296  		return []dbmodel.OperationDTO{}, dberr.Internal("Failed to get operations: %s", err)
   297  	}
   298  	return operations, nil
   299  }
   300  
   301  func (r readSession) GetOperationsByInstanceID(inID string) ([]dbmodel.OperationDTO, dberr.Error) {
   302  	idCondition := dbr.Eq("instance_id", inID)
   303  	var operations []dbmodel.OperationDTO
   304  
   305  	_, err := r.session.
   306  		Select("*").
   307  		From(OperationTableName).
   308  		Where(idCondition).
   309  		OrderDesc(CreatedAtField).
   310  		Load(&operations)
   311  
   312  	if err != nil {
   313  		return []dbmodel.OperationDTO{}, dberr.Internal("Failed to get operations: %s", err)
   314  	}
   315  	return operations, nil
   316  }
   317  
   318  func (r readSession) GetOperationsForIDs(opIDlist []string) ([]dbmodel.OperationDTO, dberr.Error) {
   319  	var operations []dbmodel.OperationDTO
   320  
   321  	_, err := r.session.
   322  		Select("*").
   323  		From(OperationTableName).
   324  		Where("id IN ?", opIDlist).
   325  		Load(&operations)
   326  	if err != nil {
   327  		return nil, dberr.Internal("Failed to get operations: %s", err)
   328  	}
   329  	return operations, nil
   330  }
   331  
   332  func (r readSession) ListOperationsByType(operationType internal.OperationType) ([]dbmodel.OperationDTO, dberr.Error) {
   333  	typeCondition := dbr.Eq("type", operationType)
   334  	var operations []dbmodel.OperationDTO
   335  
   336  	_, err := r.session.
   337  		Select("*").
   338  		From(OperationTableName).
   339  		Where(typeCondition).
   340  		Load(&operations)
   341  	if err != nil {
   342  		return nil, dberr.Internal("Failed to get operations: %s", err)
   343  	}
   344  	return operations, nil
   345  }
   346  
   347  func (r readSession) ListOperationsByOrchestrationID(orchestrationID string, filter dbmodel.OperationFilter) ([]dbmodel.OperationDTO, int, int, error) {
   348  	var ops []dbmodel.OperationDTO
   349  	condition := dbr.Eq("orchestration_id", orchestrationID)
   350  
   351  	stmt := r.session.
   352  		Select("o.*").
   353  		From(dbr.I(OperationTableName).As("o")).
   354  		Where(condition).
   355  		OrderBy("o.created_at")
   356  
   357  	// Add pagination if provided
   358  	if filter.Page > 0 && filter.PageSize > 0 {
   359  		stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize))
   360  	}
   361  
   362  	// Apply filtering if provided
   363  	addOperationFilters(stmt, filter)
   364  
   365  	_, err := stmt.Load(&ops)
   366  	if err != nil {
   367  		return nil, -1, -1, dberr.Internal("Failed to get operations: %s", err)
   368  	}
   369  
   370  	totalCount, err := r.getUpgradeOperationCount(orchestrationID, filter)
   371  	if err != nil {
   372  		return nil, -1, -1, err
   373  	}
   374  
   375  	return ops,
   376  		len(ops),
   377  		totalCount,
   378  		nil
   379  }
   380  
   381  func (r readSession) ListOperationsInTimeRange(from, to time.Time) ([]dbmodel.OperationDTO, error) {
   382  	var ops []dbmodel.OperationDTO
   383  	condition := dbr.Or(
   384  		dbr.And(dbr.Gte("created_at", from), dbr.Lte("created_at", to)),
   385  		dbr.And(dbr.Gte("updated_at", from), dbr.Lte("updated_at", to)),
   386  	)
   387  
   388  	stmt := r.session.
   389  		Select("*").
   390  		From(OperationTableName).
   391  		Where(condition)
   392  
   393  	_, err := stmt.Load(&ops)
   394  	if err != nil {
   395  		return nil, dberr.Internal("Failed to get operations: %s", err)
   396  	}
   397  
   398  	return ops, nil
   399  }
   400  
   401  func (r readSession) GetRuntimeStateByOperationID(operationID string) (dbmodel.RuntimeStateDTO, dberr.Error) {
   402  	var state dbmodel.RuntimeStateDTO
   403  
   404  	err := r.session.
   405  		Select("*").
   406  		From(RuntimeStateTableName).
   407  		Where(dbr.Eq("operation_id", operationID)).
   408  		LoadOne(&state)
   409  
   410  	if err != nil {
   411  		if err == dbr.ErrNotFound {
   412  			return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err)
   413  		}
   414  		return dbmodel.RuntimeStateDTO{}, dberr.Internal("Failed to get runtime state: %s", err)
   415  	}
   416  	return state, nil
   417  }
   418  
   419  func (r readSession) ListRuntimeStateByRuntimeID(runtimeID string) ([]dbmodel.RuntimeStateDTO, dberr.Error) {
   420  	stateCondition := dbr.Eq("runtime_id", runtimeID)
   421  	var states []dbmodel.RuntimeStateDTO
   422  
   423  	_, err := r.session.
   424  		Select("*").
   425  		From(RuntimeStateTableName).
   426  		Where(stateCondition).
   427  		OrderDesc(CreatedAtField).
   428  		Load(&states)
   429  	if err != nil {
   430  		return nil, dberr.Internal("Failed to get states: %s", err)
   431  	}
   432  	return states, nil
   433  }
   434  
   435  func (r readSession) GetLatestRuntimeStateByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) {
   436  	var state dbmodel.RuntimeStateDTO
   437  
   438  	count, err := r.session.
   439  		Select("*").
   440  		From(RuntimeStateTableName).
   441  		Where(dbr.Eq("runtime_id", runtimeID)).
   442  		OrderDesc(CreatedAtField).
   443  		Limit(1).
   444  		Load(&state)
   445  	if err != nil {
   446  		if err == dbr.ErrNotFound {
   447  			return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err)
   448  		}
   449  		return dbmodel.RuntimeStateDTO{}, dberr.Internal("Failed to get the latest runtime state: %s", err)
   450  	}
   451  	if count == 0 {
   452  		return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err)
   453  	}
   454  	return state, nil
   455  }
   456  
   457  func (r readSession) GetLatestRuntimeStateWithReconcilerInputByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) {
   458  	var state dbmodel.RuntimeStateDTO
   459  	runtimeIDIsEqual := dbr.Eq("runtime_id", runtimeID)
   460  	reconcilerInputIsNotEmptyString := dbr.Neq("cluster_setup", "")
   461  	reconcilerInputIsNotNil := dbr.Neq("cluster_setup", nil)
   462  	innerCondition := dbr.And(reconcilerInputIsNotEmptyString, reconcilerInputIsNotNil)
   463  	condition := dbr.And(runtimeIDIsEqual, innerCondition)
   464  
   465  	count, err := r.session.
   466  		Select("*").
   467  		From(RuntimeStateTableName).
   468  		Where(condition).
   469  		OrderDesc(CreatedAtField).
   470  		Limit(1).
   471  		Load(&state)
   472  	if err != nil {
   473  		if err == dbr.ErrNotFound {
   474  			return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state: %s", err)
   475  		}
   476  		return dbmodel.RuntimeStateDTO{}, dberr.Internal("Failed to get the latest runtime state with reconciler input: %s", err)
   477  	}
   478  	if count == 0 {
   479  		return dbmodel.RuntimeStateDTO{}, dberr.NotFound("cannot find runtime state with reconciler input: %s", err)
   480  	}
   481  	return state, nil
   482  }
   483  
   484  func (r readSession) GetLatestRuntimeStateWithKymaVersionByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) {
   485  	var state dbmodel.RuntimeStateDTO
   486  	condition := dbr.And(dbr.Eq("runtime_id", runtimeID),
   487  		dbr.And(dbr.Neq("kyma_version", nil), dbr.Neq("kyma_version", "")),
   488  	)
   489  
   490  	count, err := r.session.
   491  		Select("*").
   492  		From(RuntimeStateTableName).
   493  		Where(condition).
   494  		OrderDesc(CreatedAtField).
   495  		Limit(1).
   496  		Load(&state)
   497  	if err != nil {
   498  		if err == dbr.ErrNotFound {
   499  			return state, dberr.NotFound("cannot find latest runtime state with kyma version: %s", err)
   500  		}
   501  		return state, dberr.Internal("Failed to get the latest runtime state with kyma version: %s", err)
   502  	}
   503  	if count == 0 {
   504  		return state, dberr.NotFound("found 0 latest runtime states with kyma version: %s", err)
   505  	}
   506  	return state, nil
   507  }
   508  
   509  func (r readSession) GetLatestRuntimeStateWithOIDCConfigByRuntimeID(runtimeID string) (dbmodel.RuntimeStateDTO, dberr.Error) {
   510  	var state dbmodel.RuntimeStateDTO
   511  	condition := dbr.And(dbr.Eq("runtime_id", runtimeID),
   512  		dbr.Expr("cluster_config::json->>'oidcConfig' != ?", "null"),
   513  	)
   514  
   515  	count, err := r.session.
   516  		Select("*").
   517  		From(RuntimeStateTableName).
   518  		Where(condition).
   519  		OrderDesc(CreatedAtField).
   520  		Limit(1).
   521  		Load(&state)
   522  	if err != nil {
   523  		if err == dbr.ErrNotFound {
   524  			return state, dberr.NotFound("cannot find latest runtime state with OIDC config: %s", err)
   525  		}
   526  		return state, dberr.Internal("failed to get the latest runtime state with OIDC config: %s", err)
   527  	}
   528  	if count == 0 {
   529  		return state, dberr.NotFound("found 0 latest runtime states with OIDC config: %s", err)
   530  	}
   531  	return state, nil
   532  }
   533  
   534  func (r readSession) getOperation(condition dbr.Builder) (dbmodel.OperationDTO, dberr.Error) {
   535  	var operation dbmodel.OperationDTO
   536  
   537  	err := r.session.
   538  		Select("*").
   539  		From(OperationTableName).
   540  		Where(condition).
   541  		LoadOne(&operation)
   542  
   543  	if err != nil {
   544  		if err == dbr.ErrNotFound {
   545  			return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err)
   546  		}
   547  		return dbmodel.OperationDTO{}, dberr.Internal("Failed to get operation: %s", err)
   548  	}
   549  	return operation, nil
   550  }
   551  
   552  func (r readSession) getLastOperation(condition dbr.Builder) (dbmodel.OperationDTO, dberr.Error) {
   553  	var operation dbmodel.OperationDTO
   554  
   555  	count, err := r.session.
   556  		Select("*").
   557  		From(OperationTableName).
   558  		Where(condition).
   559  		OrderDesc(CreatedAtField).
   560  		Limit(1).
   561  		Load(&operation)
   562  	if err != nil {
   563  		if err == dbr.ErrNotFound {
   564  			return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err)
   565  		}
   566  		return dbmodel.OperationDTO{}, dberr.Internal("Failed to get operation: %s", err)
   567  	}
   568  	if count == 0 {
   569  		return dbmodel.OperationDTO{}, dberr.NotFound("cannot find operation: %s", err)
   570  	}
   571  
   572  	return operation, nil
   573  }
   574  
   575  func (r readSession) getOrchestration(condition dbr.Builder) (dbmodel.OrchestrationDTO, dberr.Error) {
   576  	var operation dbmodel.OrchestrationDTO
   577  
   578  	err := r.session.
   579  		Select("*").
   580  		From(OrchestrationTableName).
   581  		Where(condition).
   582  		LoadOne(&operation)
   583  
   584  	if err != nil {
   585  		if err == dbr.ErrNotFound {
   586  			return dbmodel.OrchestrationDTO{}, dberr.NotFound("cannot find operation: %s", err)
   587  		}
   588  		return dbmodel.OrchestrationDTO{}, dberr.Internal("Failed to get operation: %s", err)
   589  	}
   590  	return operation, nil
   591  }
   592  
   593  func (r readSession) GetOperationStats() ([]dbmodel.OperationStatEntry, error) {
   594  	var rows []dbmodel.OperationStatEntry
   595  	_, err := r.session.SelectBySql(fmt.Sprintf("select type, state, provisioning_parameters ->> 'plan_id' AS plan_id from %s",
   596  		OperationTableName)).Load(&rows)
   597  	return rows, err
   598  }
   599  
   600  func (r readSession) GetOperationStatsForOrchestration(orchestrationID string) ([]dbmodel.OperationStatEntry, error) {
   601  	var rows []dbmodel.OperationStatEntry
   602  	_, err := r.session.SelectBySql(fmt.Sprintf("select type, state, instance_id, provisioning_parameters ->> 'plan_id' AS plan_id from %s where orchestration_id='%s'",
   603  		OperationTableName, orchestrationID)).Load(&rows)
   604  	return rows, err
   605  }
   606  
   607  func (r readSession) GetInstanceStats() ([]dbmodel.InstanceByGlobalAccountIDStatEntry, error) {
   608  	var rows []dbmodel.InstanceByGlobalAccountIDStatEntry
   609  	_, err := r.session.SelectBySql(fmt.Sprintf("select global_account_id, count(*) as total from %s where deleted_at = '0001-01-01T00:00:00.000Z' group by global_account_id",
   610  		InstancesTableName)).Load(&rows)
   611  	return rows, err
   612  }
   613  
   614  func (r readSession) GetERSContextStats() ([]dbmodel.InstanceERSContextStatsEntry, error) {
   615  	var rows []dbmodel.InstanceERSContextStatsEntry
   616  	// group existing instances by license_Type from the last operation that is not pending or canceled
   617  	_, err := r.session.SelectBySql(`
   618  SELECT license_type, count(1) as total
   619  FROM (
   620      SELECT DISTINCT ON (instances.instance_id) instances.instance_id, operations.id, state, type, (operations.provisioning_parameters->'ers_context'->'license_type')::VARCHAR AS license_type
   621      FROM operations
   622      INNER JOIN instances
   623      ON operations.instance_id = instances.instance_id
   624      WHERE (operations.state != 'pending' OR operations.state != 'canceled') AND deleted_at = '0001-01-01T00:00:00.000Z'
   625      ORDER BY instance_id, operations.created_at DESC
   626  ) t
   627  GROUP BY license_type;
   628  `).Load(&rows)
   629  	return rows, err
   630  }
   631  
   632  func (r readSession) GetNumberOfInstancesForGlobalAccountID(globalAccountID string) (int, error) {
   633  	var res struct {
   634  		Total int
   635  	}
   636  	err := r.session.Select("count(*) as total").
   637  		From(InstancesTableName).
   638  		Where(dbr.Eq("global_account_id", globalAccountID)).
   639  		Where(dbr.Eq("deleted_at", "0001-01-01T00:00:00.000Z")).
   640  		LoadOne(&res)
   641  
   642  	return res.Total, err
   643  }
   644  
   645  func (r readSession) ListInstances(filter dbmodel.InstanceFilter) ([]dbmodel.InstanceDTO, int, int, error) {
   646  	var instances []dbmodel.InstanceDTO
   647  
   648  	// Base select and order by created at
   649  	var stmt *dbr.SelectStmt
   650  	// Find and join the last operation for each instance matching the state filter(s).
   651  	// Last operation is found with the greatest-n-per-group problem solved with OUTER JOIN, followed by a (INNER) JOIN to get instance columns.
   652  	stmt = r.session.
   653  		Select(fmt.Sprintf("%s.*", InstancesTableName)).
   654  		From(InstancesTableName).
   655  		Join(dbr.I(OperationTableName).As("o1"), fmt.Sprintf("%s.instance_id = o1.instance_id", InstancesTableName)).
   656  		LeftJoin(dbr.I(OperationTableName).As("o2"), fmt.Sprintf("%s.instance_id = o2.instance_id AND o1.created_at < o2.created_at AND o2.state NOT IN ('%s', '%s')", InstancesTableName, orchestration.Pending, orchestration.Canceled)).
   657  		Where("o2.created_at IS NULL").
   658  		Where(fmt.Sprintf("o1.state NOT IN ('%s', '%s')", orchestration.Pending, orchestration.Canceled)).
   659  		OrderBy(fmt.Sprintf("%s.%s", InstancesTableName, CreatedAtField))
   660  
   661  	if len(filter.States) > 0 {
   662  		stateFilters := buildInstanceStateFilters("o1", filter)
   663  		stmt.Where(stateFilters)
   664  	}
   665  
   666  	// Add pagination
   667  	if filter.Page > 0 && filter.PageSize > 0 {
   668  		stmt = stmt.Paginate(uint64(filter.Page), uint64(filter.PageSize))
   669  	}
   670  
   671  	addInstanceFilters(stmt, filter)
   672  
   673  	_, err := stmt.Load(&instances)
   674  	if err != nil {
   675  		return nil, -1, -1, fmt.Errorf("while fetching instances: %w", err)
   676  	}
   677  
   678  	totalCount, err := r.getInstanceCount(filter)
   679  	if err != nil {
   680  		return nil, -1, -1, err
   681  	}
   682  
   683  	return instances,
   684  		len(instances),
   685  		totalCount,
   686  		nil
   687  }
   688  
   689  func (r readSession) ListEvents(filter events.EventFilter) ([]events.EventDTO, error) {
   690  	var events []events.EventDTO
   691  	stmt := r.session.Select("*").From("events")
   692  	if len(filter.InstanceIDs) != 0 {
   693  		stmt.Where(dbr.Eq("instance_id", filter.InstanceIDs))
   694  	}
   695  	if len(filter.OperationIDs) != 0 {
   696  		stmt.Where(dbr.Eq("operation_id", filter.OperationIDs))
   697  	}
   698  	stmt.OrderBy("created_at")
   699  	_, err := stmt.Load(&events)
   700  	return events, err
   701  }
   702  
   703  func (r readSession) getInstanceCount(filter dbmodel.InstanceFilter) (int, error) {
   704  	var res struct {
   705  		Total int
   706  	}
   707  	var stmt *dbr.SelectStmt
   708  	stmt = r.session.
   709  		Select("count(*) as total").
   710  		From(InstancesTableName).
   711  		Join(dbr.I(OperationTableName).As("o1"), fmt.Sprintf("%s.instance_id = o1.instance_id", InstancesTableName)).
   712  		LeftJoin(dbr.I(OperationTableName).As("o2"), fmt.Sprintf("%s.instance_id = o2.instance_id AND o1.created_at < o2.created_at AND o2.state NOT IN ('%s', '%s')", InstancesTableName, orchestration.Pending, orchestration.Canceled)).
   713  		Where("o2.created_at IS NULL").
   714  		Where(fmt.Sprintf("o1.state NOT IN ('%s', '%s')", orchestration.Pending, orchestration.Canceled))
   715  
   716  	if len(filter.States) > 0 {
   717  		stateFilters := buildInstanceStateFilters("o1", filter)
   718  		stmt.Where(stateFilters)
   719  	}
   720  
   721  	addInstanceFilters(stmt, filter)
   722  	err := stmt.LoadOne(&res)
   723  
   724  	return res.Total, err
   725  }
   726  
   727  func buildInstanceStateFilters(table string, filter dbmodel.InstanceFilter) dbr.Builder {
   728  	var exprs []dbr.Builder
   729  	for _, s := range filter.States {
   730  		switch s {
   731  		case dbmodel.InstanceSucceeded:
   732  			exprs = append(exprs, dbr.And(
   733  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.Succeeded),
   734  				dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision),
   735  			))
   736  		case dbmodel.InstanceFailed:
   737  			exprs = append(exprs, dbr.And(
   738  				dbr.Or(
   739  					dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeProvision),
   740  					dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision),
   741  				),
   742  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.Failed),
   743  			))
   744  		case dbmodel.InstanceError:
   745  			exprs = append(exprs, dbr.And(
   746  				dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeProvision),
   747  				dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision),
   748  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.Failed),
   749  			))
   750  		case dbmodel.InstanceProvisioning:
   751  			exprs = append(exprs, dbr.And(
   752  				dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeProvision),
   753  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress),
   754  			))
   755  		case dbmodel.InstanceDeprovisioning:
   756  			exprs = append(exprs, dbr.And(
   757  				dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision),
   758  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress),
   759  			))
   760  		case dbmodel.InstanceUpgrading:
   761  			exprs = append(exprs, dbr.And(
   762  				dbr.Like(fmt.Sprintf("%s.type", table), "upgrade%"),
   763  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress),
   764  			))
   765  		case dbmodel.InstanceUpdating:
   766  			exprs = append(exprs, dbr.And(
   767  				dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeUpdate),
   768  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.InProgress),
   769  			))
   770  		case dbmodel.InstanceDeprovisioned:
   771  			exprs = append(exprs, dbr.And(
   772  				dbr.Eq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision),
   773  				dbr.Eq(fmt.Sprintf("%s.state", table), domain.Succeeded),
   774  			))
   775  		case dbmodel.InstanceNotDeprovisioned:
   776  			exprs = append(exprs, dbr.Or(
   777  				dbr.Neq(fmt.Sprintf("%s.type", table), internal.OperationTypeDeprovision),
   778  				dbr.Neq(fmt.Sprintf("%s.state", table), domain.Succeeded),
   779  			))
   780  		}
   781  	}
   782  
   783  	return dbr.Or(exprs...)
   784  }
   785  
   786  func addInstanceFilters(stmt *dbr.SelectStmt, filter dbmodel.InstanceFilter) {
   787  	if len(filter.GlobalAccountIDs) > 0 {
   788  		stmt.Where("instances.global_account_id IN ?", filter.GlobalAccountIDs)
   789  	}
   790  	if len(filter.SubAccountIDs) > 0 {
   791  		stmt.Where("instances.sub_account_id IN ?", filter.SubAccountIDs)
   792  	}
   793  	if len(filter.InstanceIDs) > 0 {
   794  		stmt.Where("instances.instance_id IN ?", filter.InstanceIDs)
   795  	}
   796  	if len(filter.RuntimeIDs) > 0 {
   797  		stmt.Where("instances.runtime_id IN ?", filter.RuntimeIDs)
   798  	}
   799  	if len(filter.Regions) > 0 {
   800  		stmt.Where("instances.provider_region IN ?", filter.Regions)
   801  	}
   802  	if len(filter.Plans) > 0 {
   803  		stmt.Where("instances.service_plan_name IN ?", filter.Plans)
   804  	}
   805  	if len(filter.PlanIDs) > 0 {
   806  		stmt.Where("instances.service_plan_id IN ?", filter.PlanIDs)
   807  	}
   808  	if len(filter.Shoots) > 0 {
   809  		shootNameMatch := fmt.Sprintf(`^(%s)$`, strings.Join(filter.Shoots, "|"))
   810  		stmt.Where("o1.data::json->>'shoot_name' ~ ?", shootNameMatch)
   811  	}
   812  
   813  	if filter.Expired != nil {
   814  		if *filter.Expired {
   815  			stmt.Where("instances.expired_at IS NOT NULL")
   816  		}
   817  		if !*filter.Expired {
   818  			stmt.Where("instances.expired_at IS NULL")
   819  		}
   820  	}
   821  
   822  	if filter.DeletionAttempted != nil {
   823  		if *filter.DeletionAttempted {
   824  			stmt.Where("instances.deleted_at != '0001-01-01T00:00:00.000Z'")
   825  		}
   826  		if !*filter.DeletionAttempted {
   827  			stmt.Where("instances.deleted_at = '0001-01-01T00:00:00.000Z'")
   828  		}
   829  	}
   830  }
   831  
   832  func addOrchestrationFilters(stmt *dbr.SelectStmt, filter dbmodel.OrchestrationFilter) {
   833  	if len(filter.Types) > 0 {
   834  		stmt.Where("type IN ?", filter.Types)
   835  	}
   836  	if len(filter.States) > 0 {
   837  		stmt.Where("state IN ?", filter.States)
   838  	}
   839  }
   840  
   841  func addOperationFilters(stmt *dbr.SelectStmt, filter dbmodel.OperationFilter) {
   842  	if len(filter.States) > 0 {
   843  		stmt.Where("o.state IN ?", filter.States)
   844  	}
   845  	if filter.InstanceFilter != nil {
   846  		fi := filter.InstanceFilter
   847  		if slices.Contains(filter.States, string(dbmodel.InstanceDeprovisioned)) {
   848  			stmt.LeftJoin(dbr.I(InstancesTableName).As("i"), "i.instance_id = o.instance_id").
   849  				Where("i.instance_id IS NULL")
   850  		}
   851  		if len(fi.InstanceIDs) != 0 {
   852  			stmt.Where("o.instance_id IN ?", fi.InstanceIDs)
   853  		}
   854  	}
   855  }
   856  
   857  func (r readSession) getOperationCount(filter dbmodel.OperationFilter) (int, error) {
   858  	var res struct {
   859  		Total int
   860  	}
   861  	stmt := r.session.Select("count(1) as total").
   862  		From(dbr.I(OperationTableName).As("o"))
   863  	addOperationFilters(stmt, filter)
   864  	err := stmt.LoadOne(&res)
   865  
   866  	return res.Total, err
   867  }
   868  
   869  func (r readSession) getUpgradeOperationCount(orchestrationID string, filter dbmodel.OperationFilter) (int, error) {
   870  	var res struct {
   871  		Total int
   872  	}
   873  	stmt := r.session.Select("count(1) as total").
   874  		From(dbr.I(OperationTableName).As("o")).
   875  		Where(dbr.Eq("o.orchestration_id", orchestrationID))
   876  	addOperationFilters(stmt, filter)
   877  	err := stmt.LoadOne(&res)
   878  
   879  	return res.Total, err
   880  }
   881  
   882  func (r readSession) getOrchestrationCount(filter dbmodel.OrchestrationFilter) (int, error) {
   883  	var res struct {
   884  		Total int
   885  	}
   886  	stmt := r.session.Select("count(*) as total").From(OrchestrationTableName)
   887  	addOrchestrationFilters(stmt, filter)
   888  	err := stmt.LoadOne(&res)
   889  
   890  	return res.Total, err
   891  }