/*-------------------------------------------------------------------------
 *
 * deparse_schema_stmts.c
 *	  All routines to deparse schema statements.
 *	  This file contains all entry points specific for schema statement deparsing
 *	  as well as functions that are currently only used for deparsing of the
 *	  schema statements.
 *
 * Copyright (c) Citus Data, Inc.
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "lib/stringinfo.h"
#include "nodes/nodes.h"
#include "utils/builtins.h"

#include "distributed/citus_ruleutils.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"

static void AppendCreateSchemaStmt(StringInfo buf, CreateSchemaStmt *stmt);
static void AppendDropSchemaStmt(StringInfo buf, DropStmt *stmt);
static void AppendGrantOnSchemaStmt(StringInfo buf, GrantStmt *stmt);
static void AppendGrantOnSchemaSchemas(StringInfo buf, GrantStmt *stmt);
static void AppendAlterSchemaRenameStmt(StringInfo buf, RenameStmt *stmt);
static void AppendAlterSchemaOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);

char *
DeparseCreateSchemaStmt(Node *node)
{
	CreateSchemaStmt *stmt = castNode(CreateSchemaStmt, node);

	StringInfoData str = { 0 };
	initStringInfo(&str);

	AppendCreateSchemaStmt(&str, stmt);

	return str.data;
}


char *
DeparseDropSchemaStmt(Node *node)
{
	DropStmt *stmt = castNode(DropStmt, node);

	StringInfoData str = { 0 };
	initStringInfo(&str);

	AppendDropSchemaStmt(&str, stmt);

	return str.data;
}


char *
DeparseGrantOnSchemaStmt(Node *node)
{
	GrantStmt *stmt = castNode(GrantStmt, node);
	Assert(stmt->objtype == OBJECT_SCHEMA);

	StringInfoData str = { 0 };
	initStringInfo(&str);

	AppendGrantOnSchemaStmt(&str, stmt);

	return str.data;
}


char *
DeparseAlterSchemaOwnerStmt(Node *node)
{
	AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);

	StringInfoData str = { 0 };
	initStringInfo(&str);

	AppendAlterSchemaOwnerStmt(&str, stmt);

	return str.data;
}


static void
AppendAlterSchemaOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)
{
	Assert(stmt->objectType == OBJECT_SCHEMA);

	appendStringInfo(buf, "ALTER SCHEMA %s OWNER TO %s;",
					 quote_identifier(strVal(stmt->object)),
					 RoleSpecString(stmt->newowner, true));
}


char *
DeparseAlterSchemaRenameStmt(Node *node)
{
	RenameStmt *stmt = castNode(RenameStmt, node);

	StringInfoData str = { 0 };
	initStringInfo(&str);

	AppendAlterSchemaRenameStmt(&str, stmt);

	return str.data;
}


static void
AppendCreateSchemaStmt(StringInfo buf, CreateSchemaStmt *stmt)
{
	appendStringInfoString(buf, "CREATE SCHEMA ");

	if (stmt->if_not_exists)
	{
		appendStringInfoString(buf, "IF NOT EXISTS ");
	}

	if (stmt->schemaname != NULL)
	{
		appendStringInfo(buf, "%s ", quote_identifier(stmt->schemaname));
	}
	else
	{
		/*
		 * If the schema name is not provided, the schema will be created
		 * with the name of the authorizated user.
		 */
		Assert(stmt->authrole != NULL);
	}

	if (stmt->authrole != NULL)
	{
		appendStringInfo(buf, "AUTHORIZATION %s", RoleSpecString(stmt->authrole, true));
	}
}


static void
AppendDropSchemaStmt(StringInfo buf, DropStmt *stmt)
{
	Assert(stmt->removeType == OBJECT_SCHEMA);

	appendStringInfoString(buf, "DROP SCHEMA ");

	if (stmt->missing_ok)
	{
		appendStringInfoString(buf, "IF EXISTS ");
	}

	String *schemaValue = NULL;
	foreach_declared_ptr(schemaValue, stmt->objects)
	{
		const char *schemaString = quote_identifier(strVal(schemaValue));
		appendStringInfo(buf, "%s", schemaString);

		if (schemaValue != llast(stmt->objects))
		{
			appendStringInfoString(buf, ", ");
		}
	}

	if (stmt->behavior == DROP_CASCADE)
	{
		appendStringInfoString(buf, " CASCADE");
	}
	else if (stmt->behavior == DROP_RESTRICT)
	{
		appendStringInfoString(buf, " RESTRICT");
	}
}


static void
AppendGrantOnSchemaStmt(StringInfo buf, GrantStmt *stmt)
{
	Assert(stmt->objtype == OBJECT_SCHEMA);

	AppendGrantSharedPrefix(buf, stmt);

	AppendGrantOnSchemaSchemas(buf, stmt);

	AppendGrantSharedSuffix(buf, stmt);
}


void
AppendGrantPrivileges(StringInfo buf, GrantStmt *stmt)
{
	if (list_length(stmt->privileges) == 0)
	{
		appendStringInfo(buf, "ALL PRIVILEGES");
	}
	else
	{
		ListCell *cell = NULL;
		foreach(cell, stmt->privileges)
		{
			AccessPriv *privilege = (AccessPriv *) lfirst(cell);
			appendStringInfoString(buf, privilege->priv_name);
			if (cell != list_tail(stmt->privileges))
			{
				appendStringInfo(buf, ", ");
			}
		}
	}
}


static void
AppendGrantOnSchemaSchemas(StringInfo buf, GrantStmt *stmt)
{
	ListCell *cell = NULL;
	appendStringInfo(buf, " ON SCHEMA ");

	foreach(cell, stmt->objects)
	{
		char *schema = strVal(lfirst(cell));
		appendStringInfoString(buf, quote_identifier(schema));
		if (cell != list_tail(stmt->objects))
		{
			appendStringInfo(buf, ", ");
		}
	}
}


void
AppendGrantGrantees(StringInfo buf, GrantStmt *stmt)
{
	ListCell *cell = NULL;
	appendStringInfo(buf, " %s ", stmt->is_grant ? "TO" : "FROM");

	foreach(cell, stmt->grantees)
	{
		RoleSpec *grantee = (RoleSpec *) lfirst(cell);
		appendStringInfoString(buf, RoleSpecString(grantee, true));
		if (cell != list_tail(stmt->grantees))
		{
			appendStringInfo(buf, ", ");
		}
	}
}


static void
AppendAlterSchemaRenameStmt(StringInfo buf, RenameStmt *stmt)
{
	Assert(stmt->renameType == OBJECT_SCHEMA);

	appendStringInfo(buf, "ALTER SCHEMA %s RENAME TO %s;",
					 quote_identifier(stmt->subname), quote_identifier(stmt->newname));
}
