package orm

import (
	

	
)

// Placeholder that is replaced with count(*).
const placeholder = `'_go_pg_placeholder'`

// https://wiki.postgresql.org/wiki/Count_estimate
//nolint
var pgCountEstimateFunc = fmt.Sprintf(`
CREATE OR REPLACE FUNCTION _go_pg_count_estimate_v2(query text, threshold int)
RETURNS int AS $$
DECLARE
  rec record;
  nrows int;
BEGIN
  FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
    nrows := substring(rec."QUERY PLAN" FROM ' rows=(\d+)');
    EXIT WHEN nrows IS NOT NULL;
  END LOOP;

  -- Return the estimation if there are too many rows.
  IF nrows > threshold THEN
    RETURN nrows;
  END IF;

  -- Otherwise execute real count query.
  query := replace(query, 'SELECT '%s'', 'SELECT count(*)');
  EXECUTE query INTO nrows;

  IF nrows IS NULL THEN
    nrows := 0;
  END IF;

  RETURN nrows;
END;
$$ LANGUAGE plpgsql;
`, placeholder)

// CountEstimate uses EXPLAIN to get estimated number of rows returned the query.
// If that number is bigger than the threshold it returns the estimation.
// Otherwise it executes another query using count aggregate function and
// returns the result.
//
// Based on https://wiki.postgresql.org/wiki/Count_estimate
func ( *Query) ( int) (int, error) {
	if .stickyErr != nil {
		return 0, .stickyErr
	}

	,  := .countSelectQuery(placeholder).AppendQuery(.db.Formatter(), nil)
	if  != nil {
		return 0, 
	}

	for  := 0;  < 3; ++ {
		var  int
		_,  = .db.QueryOneContext(
			.ctx,
			Scan(&),
			"SELECT _go_pg_count_estimate_v2(?, ?)",
			string(), ,
		)
		if  != nil {
			if ,  := .(internal.PGError);  && .Field('C') == "42883" {
				// undefined_function
				 = .createCountEstimateFunc()
				if  != nil {
					,  := .(internal.PGError)
					if ! || !.IntegrityViolation() {
						return 0, 
					}
				}
				continue
			}
		}
		return , 
	}

	return 0, 
}

func ( *Query) () error {
	,  := .db.ExecContext(.ctx, pgCountEstimateFunc)
	return 
}