/*****************************************************************************\
 *  as_mysql_problems.c - functions for finding out problems in the
 *                     associations and other places in the database.
 *****************************************************************************
 *  Copyright (C) 2009 Lawrence Livermore National Security.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Danny Auble <da@llnl.gov>
 *  CODE-OCEC-09-009. All rights reserved.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#include "as_mysql_problems.h"
#include "src/common/uid.h"

static int _setup_assoc_cond_limits(
	slurmdb_assoc_cond_t *assoc_cond,
	char **extra, bool user_query)
{
	int set = 0;
	list_itr_t *itr = NULL;
	char *object = NULL;

	xstrfmtcat(*extra, "where t1.deleted=0");

	if (!assoc_cond)
		return 0;

	if (assoc_cond->acct_list && list_count(assoc_cond->acct_list)) {
		set = 0;
		xstrcat(*extra, " && (");
		itr = list_iterator_create(assoc_cond->acct_list);
		while ((object = list_next(itr))) {
			if (set)
				xstrcat(*extra, " || ");
			xstrfmtcat(*extra, "t1.acct='%s'", object);
			set = 1;
		}
		list_iterator_destroy(itr);
		xstrcat(*extra, ")");
	}

	if (assoc_cond->user_list && list_count(assoc_cond->user_list)) {
		set = 0;
		xstrcat(*extra, " && (");
		itr = list_iterator_create(assoc_cond->user_list);
		while ((object = list_next(itr))) {
			if (set)
				xstrcat(*extra, " || ");
			xstrfmtcat(*extra, "t1.user='%s'", object);
			set = 1;
		}
		list_iterator_destroy(itr);
		xstrcat(*extra, ")");
	} else if (user_query) {
		/* we want all the users, but no non-user associations */
		set = 1;
		xstrcat(*extra, " && (t1.user!='')");
	}

	if (assoc_cond->partition_list
	    && list_count(assoc_cond->partition_list)) {
		set = 0;
		xstrcat(*extra, " && (");
		itr = list_iterator_create(assoc_cond->partition_list);
		while ((object = list_next(itr))) {
			if (set)
				xstrcat(*extra, " || ");
			xstrfmtcat(*extra, "t1.`partition`='%s'", object);
			set = 1;
		}
		list_iterator_destroy(itr);
		xstrcat(*extra, ")");
	}

	return set;
}


extern int as_mysql_acct_no_assocs(mysql_conn_t *mysql_conn,
				   slurmdb_assoc_cond_t *assoc_cond,
				   list_t *ret_list)
{
	int rc = SLURM_SUCCESS;
	char *query = NULL;
	MYSQL_RES *result = NULL;
	MYSQL_ROW row;
	list_t *use_cluster_list = NULL;
	list_itr_t *itr = NULL;
	char *cluster_name = NULL;
	bool locked = false;

	xassert(ret_list);

	query = xstrdup_printf("select name from %s where deleted=0",
			       acct_table);
	if (assoc_cond &&
	    assoc_cond->acct_list && list_count(assoc_cond->acct_list)) {
		int set = 0;
		list_itr_t *itr = NULL;
		char *object = NULL;
		xstrcat(query, " && (");
		itr = list_iterator_create(assoc_cond->acct_list);
		while ((object = list_next(itr))) {
			if (set)
				xstrcat(query, " || ");
			xstrfmtcat(query, "name='%s'", object);
			set = 1;
		}
		list_iterator_destroy(itr);
		xstrcat(query, ")");
	}

	if (!(result = mysql_db_query_ret(
		      mysql_conn, query, 0))) {
		xfree(query);
		return ESLURM_BAD_SQL;
	}
	xfree(query);

	if (assoc_cond &&
	    assoc_cond->cluster_list && list_count(assoc_cond->cluster_list))
		use_cluster_list = assoc_cond->cluster_list;
	else {
		slurm_rwlock_rdlock(&as_mysql_cluster_list_lock);
		use_cluster_list = list_shallow_copy(as_mysql_cluster_list);
		locked = true;
	}

	itr = list_iterator_create(use_cluster_list);
	while ((row = mysql_fetch_row(result))) {
		MYSQL_RES *result2 = NULL;
		int cnt = 0;
		slurmdb_assoc_rec_t *assoc = NULL;

		/* See if we have at least 1 association in the system */
		while ((cluster_name = list_next(itr))) {
			if (query)
				xstrcat(query, " union ");
			xstrfmtcat(query,
				   "select distinct id_assoc from \"%s_%s\" "
				   "where deleted=0 && "
				   "acct='%s'",
				   cluster_name, assoc_table, row[0]);
		}
		list_iterator_reset(itr);
		if (query)
			xstrcat(query, " limit 1");
		if (!(result2 = mysql_db_query_ret(
			      mysql_conn, query, 0))) {
			xfree(query);
			rc = ESLURM_BAD_SQL;
			break;
		}
		xfree(query);

		cnt = mysql_num_rows(result2);
		mysql_free_result(result2);

		if (cnt)
			continue;

		assoc =	xmalloc(sizeof(slurmdb_assoc_rec_t));
		list_append(ret_list, assoc);

		assoc->id = SLURMDB_PROBLEM_ACCT_NO_ASSOC;
		assoc->acct = xstrdup(row[0]);
	}
	mysql_free_result(result);

	list_iterator_destroy(itr);
	if (locked) {
		slurm_rwlock_unlock(&as_mysql_cluster_list_lock);
		FREE_NULL_LIST(use_cluster_list);
	}

	return rc;
}

extern int as_mysql_acct_no_users(mysql_conn_t *mysql_conn,
				  slurmdb_assoc_cond_t *assoc_cond,
				  list_t *ret_list)
{
	int rc = SLURM_SUCCESS;
	char *query = NULL, *tmp = NULL;
	char *extra = NULL;
	int i = 0;
	MYSQL_RES *result = NULL;
	MYSQL_ROW row;
	list_t *use_cluster_list = NULL;
	list_itr_t *itr = NULL;
	char *cluster_name;
	bool locked = false;

	xassert(ret_list);

	_setup_assoc_cond_limits(assoc_cond, &extra, 0);

	/* if this changes you will need to edit the corresponding enum */
	char *assoc_req_inx[] = {
		"id_assoc",
		"user",
		"acct",
		"`partition`",
		"parent_acct",
	};
	enum {
		ASSOC_REQ_ID,
		ASSOC_REQ_USER,
		ASSOC_REQ_ACCT,
		ASSOC_REQ_PART,
		ASSOC_REQ_PARENT,
		ASSOC_REQ_COUNT
	};

	xfree(tmp);
	xstrfmtcat(tmp, "t1.%s", assoc_req_inx[i]);
	for(i=1; i<ASSOC_REQ_COUNT; i++) {
		xstrfmtcat(tmp, ", t1.%s", assoc_req_inx[i]);
	}

	if (assoc_cond &&
	    assoc_cond->cluster_list && list_count(assoc_cond->cluster_list))
		use_cluster_list = assoc_cond->cluster_list;
	else {
		slurm_rwlock_rdlock(&as_mysql_cluster_list_lock);
		use_cluster_list = as_mysql_cluster_list;
		locked = true;
	}

	itr = list_iterator_create(use_cluster_list);
	while ((cluster_name = list_next(itr))) {
		/*
		 * Only get the account associations that don't have an
		 * associated row with a user in it
		 */
		if (query)
			xstrcat(query, " union ");

		xstrfmtcat(query, "select distinct %s, '%s' as cluster from \"%s_%s\" as t1 left join \"%s_%s\" as t2 on t2.lineage like concat(t1.lineage, '0-%%') %s && t1.user='' && t2.lineage is NULL",
			   tmp, cluster_name, cluster_name,
			   assoc_table, cluster_name,
			   assoc_table, extra);
	}
	list_iterator_destroy(itr);
	if (locked)
		slurm_rwlock_unlock(&as_mysql_cluster_list_lock);

	if (query)
		xstrcat(query, " order by cluster, acct;");

	xfree(tmp);
	xfree(extra);
	DB_DEBUG(DB_QUERY, mysql_conn->conn, "query\n%s", query);
	if (!(result = mysql_db_query_ret(
		      mysql_conn, query, 0))) {
		xfree(query);
		return ESLURM_BAD_SQL;
	}
	xfree(query);

	while ((row = mysql_fetch_row(result))) {
		slurmdb_assoc_rec_t *assoc =
			xmalloc(sizeof(slurmdb_assoc_rec_t));

		list_append(ret_list, assoc);

		assoc->id = SLURMDB_PROBLEM_ACCT_NO_USERS;

		if (row[ASSOC_REQ_USER][0])
			assoc->user = xstrdup(row[ASSOC_REQ_USER]);
		assoc->acct = xstrdup(row[ASSOC_REQ_ACCT]);
		assoc->cluster = xstrdup(row[ASSOC_REQ_COUNT]);

		if (row[ASSOC_REQ_PARENT][0])
			assoc->parent_acct = xstrdup(row[ASSOC_REQ_PARENT]);

		if (row[ASSOC_REQ_PART][0])
			assoc->partition = xstrdup(row[ASSOC_REQ_PART]);
	}
	mysql_free_result(result);

	return rc;
}

extern int as_mysql_user_no_assocs_or_no_uid(
	mysql_conn_t *mysql_conn,
	slurmdb_assoc_cond_t *assoc_cond,
	list_t *ret_list)
{
	int rc = SLURM_SUCCESS;
	char *query = NULL;
	MYSQL_RES *result = NULL;
	MYSQL_ROW row;
	list_t *use_cluster_list = NULL;
	list_itr_t *itr = NULL;
	char *cluster_name = NULL;
	bool locked = false;

	xassert(ret_list);

	query = xstrdup_printf("select name from %s where deleted=0",
			       user_table);
	if (assoc_cond &&
	    assoc_cond->user_list && list_count(assoc_cond->user_list)) {
		int set = 0;
		char *object = NULL;
		xstrcat(query, " && (");
		itr = list_iterator_create(assoc_cond->user_list);
		while ((object = list_next(itr))) {
			if (set)
				xstrcat(query, " || ");
			xstrfmtcat(query, "name='%s'", object);
			set = 1;
		}
		list_iterator_destroy(itr);
		xstrcat(query, ")");
	}

	if (!(result = mysql_db_query_ret(
		      mysql_conn, query, 0))) {
		xfree(query);
		return ESLURM_BAD_SQL;
	}
	xfree(query);

	if (assoc_cond &&
	    assoc_cond->cluster_list && list_count(assoc_cond->cluster_list))
		use_cluster_list = assoc_cond->cluster_list;
	else {
		slurm_rwlock_rdlock(&as_mysql_cluster_list_lock);
		use_cluster_list = list_shallow_copy(as_mysql_cluster_list);
		locked = true;
	}

	itr = list_iterator_create(use_cluster_list);
	while ((row = mysql_fetch_row(result))) {
		MYSQL_RES *result2 = NULL;
		int cnt = 0;
		slurmdb_assoc_rec_t *assoc = NULL;
		uid_t pw_uid;

		if (uid_from_string(row[0], &pw_uid) != SLURM_SUCCESS) {
			assoc =	xmalloc(sizeof(slurmdb_assoc_rec_t));
			list_append(ret_list, assoc);

			assoc->id = SLURMDB_PROBLEM_USER_NO_UID;
			assoc->user = xstrdup(row[0]);

			continue;
		}

		/* See if we have at least 1 association in the system */
		while ((cluster_name = list_next(itr))) {
			if (query)
				xstrcat(query, " union ");
			xstrfmtcat(query,
				   "select distinct id_assoc from \"%s_%s\" "
				   "where deleted=0 && "
				   "user='%s'",
				   cluster_name, assoc_table, row[0]);
		}
		list_iterator_reset(itr);

		if (query)
			xstrcat(query, " limit 1");

		if (!(result2 = mysql_db_query_ret(
			      mysql_conn, query, 0))) {
			xfree(query);
			rc = ESLURM_BAD_SQL;
			break;
		}
		xfree(query);

		cnt = mysql_num_rows(result2);
		mysql_free_result(result2);

		if (cnt)
			continue;

		assoc =	xmalloc(sizeof(slurmdb_assoc_rec_t));
		list_append(ret_list, assoc);

		assoc->id = SLURMDB_PROBLEM_USER_NO_ASSOC;
		assoc->user = xstrdup(row[0]);
	}
	mysql_free_result(result);

	list_iterator_destroy(itr);
	if (locked) {
		FREE_NULL_LIST(use_cluster_list);
		slurm_rwlock_unlock(&as_mysql_cluster_list_lock);
	}

	return rc;
}
