OpenSolaris_b135/cmd/mms/mm/common/mm_sql.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <mms_list.h>
#include <mms_parser.h>
#include <mms_par_impl.h>
#include <libpq-fe.h>
#include <mms_trace.h>
#include <mms_strapp.h>
#include <pthread.h>
#include "mm_db.h"
#include "mm.h"
#include "mm_util.h"
#include "mm_commands.h"
#include "mm_sql.h"
#include "mm_sql_impl.h"
#include "mm_path.h"


static char *_SrcFile = __FILE__;

extern mm_pkey_t *mm_get_pkey(char *obj);

char *mm_sql_number_buf(mm_command_t *cmd);
char *mm_sql_order_buf(mm_command_t *cmd);
static sql_ops_tab_t opstab[] = {
	"strlohi", "ASC",
	"strhilo", "DESC",
	"numlohi", "ASC",
	"numhilo", "DESC",
	"regex", "~",
	"streq", "=",
	"strne", "<>",
	"strlt", "<",
	"strle", "<=",
	"strgt", ">",
	"strge", ">=",
	"numeq", "=",
	"numne", "<>",
	"numlt", "<",
	"numle", "<=",
	"numgt", ">",
	"numge", ">=",
	"and", "AND",
	"or", "OR",
	"not", "NOT",
	"hosteq", "=",
	"hostne", "<>"
};
static int	num_ops = sizeof (opstab) / sizeof (sql_ops_tab_t);


char *
mm_get_cart_pcl(mm_command_t *cmd, char *cart_id, mm_db_t *db)
{
	char		*value;
	char		*cart_pcl;

	/*
	 * Get cartridge pcl from cartridge id
	 */
	if (cart_id == NULL) {
		mms_trace(MMS_DEVP, "cartridge id is null");
		SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize,
		    strlen(RESPONSE_ERROR) + strlen(cmd->cmd_task) +
		    strlen(ECLASS_EXPLICIT) + strlen(ENOMATCH) + 1);
		(void) snprintf(cmd->cmd_buf, cmd->cmd_bufsize,
		    RESPONSE_ERROR, cmd->cmd_task,
		    ECLASS_EXPLICIT, ENOMATCH);
		return (NULL);
	}
	if (mm_db_exec(HERE, db, "select \"CartridgePCL\" from \"CARTRIDGE\" "
	    "where \"CartridgeID\" = '%s'", cart_id) != MM_DB_DATA) {
		mm_clear_db(&db->mm_db_results);
		SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize,
		    strlen(RESPONSE_ERROR) + strlen(cmd->cmd_task) +
		    strlen(ECLASS_INTERNAL) + strlen(EDATABASE) + 1);
		(void) snprintf(cmd->cmd_buf, cmd->cmd_bufsize,
		    RESPONSE_ERROR, cmd->cmd_task,
		    ECLASS_INTERNAL, EDATABASE);
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (NULL);
	}
	if (PQntuples(db->mm_db_results) != 1) {
		mms_trace(MMS_DEVP, "cartridge pcl failed");
		SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize,
		    strlen(RESPONSE_ERROR) + strlen(cmd->cmd_task) +
		    strlen(ECLASS_EXPLICIT) + strlen(ENOMATCH) + 1);
		(void) snprintf(cmd->cmd_buf, cmd->cmd_bufsize,
		    RESPONSE_ERROR, cmd->cmd_task,
		    ECLASS_EXPLICIT, ENOMATCH);
		mm_clear_db(&db->mm_db_results);
		return (NULL);
	}
	value = PQgetvalue(db->mm_db_results, 0, 0);
	if (value[0] == '\0') {
		value = "none";
	}
	cart_pcl = strdup(value);
	mm_clear_db(&db->mm_db_results);
	if (cart_pcl == NULL) {
	no_mem:
		MM_ABORT_NO_MEM();
	}
	return (cart_pcl);
}


int
mm_set_cartridge_status(char *id, char *status, mm_db_t *db) {
	if ((id == NULL) ||
	    (status == NULL)) {
		mms_trace(MMS_ERR,
		    "id and/or status cannot be null");
		return (1);
	}
	/* Allowed values for status are : */
	/* in use */
	/* unavailable */
	/* available */
	if (mm_db_exec(HERE, db,
		    "update \"CARTRIDGE\" "
		    "set \"CartridgeStatus\" = '%s' "
		    "where \"CartridgeID\" = '%s';",
		    status, id) != MM_DB_OK) {
		mms_trace(MMS_ERR,
		    "Error updateing cartridge state, %s, %s",
		    id, status);
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (1);
	}
	return (0);
}

int
mm_set_drive_statesoft(char *drive, char *state, mm_db_t *db) {
	if ((drive == NULL) ||
	    (state == NULL)) {
		mms_trace(MMS_ERR,
		    "drive and/or state cannot be null");
		return (1);
	}
	/* Allowed values for status are : */
	/* in use */
	/* ready */
	if (mm_db_exec(HERE, db,
		    "update \"DRIVE\" "
		    "set \"DriveStateSoft\" = '%s' "
		    "where \"DriveName\" = '%s';",
		    state, drive) != MM_DB_OK) {
		mms_trace(MMS_ERR,
		    "Error updateing drive state, %s, %s",
		    drive, state);
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (1);
	}
	return (0);
}



void
mm_set_mount_info_cart(char *cart_id,
		    cmd_mount_info_t *mount_info) {
	if (mount_info->cmi_cartridge)
		free(mount_info->cmi_cartridge);
	mount_info->cmi_cartridge = NULL;
	mount_info->cmi_cartridge =
		mms_strapp(mount_info->cmi_cartridge,
		    cart_id);
}
void
mm_set_mount_info_drive(char *drive,
			cmd_mount_info_t *mount_info) {
	if (mount_info->cmi_drive)
		free(mount_info->cmi_drive);
	mount_info->cmi_drive = NULL;
	mount_info->cmi_drive =
		mms_strapp(mount_info->cmi_drive,
		    drive);
}
void
mm_set_mount_info_library(char *library,
			cmd_mount_info_t *mount_info) {
	if (mount_info->cmi_library)
		free(mount_info->cmi_library);
	mount_info->cmi_library = NULL;
	mount_info->cmi_library =
		mms_strapp(mount_info->cmi_library,
		    library);
}
void
mm_set_mount_info_dm(char *dm,
		    cmd_mount_info_t *mount_info) {
	if (mount_info->cmi_dm)
		free(mount_info->cmi_dm);
	mount_info->cmi_dm = NULL;
	mount_info->cmi_dm =
		mms_strapp(mount_info->cmi_dm,
		    dm);
}
void
mm_set_mount_info_pcl(char *pcl,
		    cmd_mount_info_t *mount_info) {
	if (mount_info->cmi_pcl)
		free(mount_info->cmi_pcl);
	mount_info->cmi_pcl = NULL;
	mount_info->cmi_pcl =
		mms_strapp(mount_info->cmi_pcl,
		    pcl);
}


void
mm_free_mount_info(cmd_mount_info_t *mount_info) {
	if (mount_info->cmi_cartridge)
		free(mount_info->cmi_cartridge);
	if (mount_info->cmi_drive)
		free(mount_info->cmi_drive);
	if (mount_info->cmi_library)
		free(mount_info->cmi_library);
	if (mount_info->cmi_dm)
		free(mount_info->cmi_dm);
	if (mount_info->cmi_pcl)
		free(mount_info->cmi_pcl);
	mount_info->cmi_cartridge = NULL;
	mount_info->cmi_pcl = NULL;
	mount_info->cmi_drive = NULL;
	mount_info->cmi_library = NULL;
	mount_info->cmi_dm = NULL;
}



void
mm_print_dm_status(mm_dm_stat_t *dm_stat) {
	mms_trace(MMS_DEVP,
	    "DM, %s, Status for Drive, %s, host, %s",
	    dm_stat->dm_stat_name,
	    dm_stat->dm_stat_drive,
	    dm_stat->dm_stat_host);
	mms_trace(MMS_DEVP,
	    "     DMStateHard = %s",
	    dm_stat->dm_stat_hard);
	mms_trace(MMS_DEVP,
	    "     DMStateSoft = %s",
	    dm_stat->dm_stat_soft);
	mms_trace(MMS_DEVP,
	    "     DMDisabled = %s",
	    dm_stat->dm_stat_disabled);
}

void
mm_free_dm_status(mm_dm_stat_t *dm_stat) {
	if (dm_stat == NULL) {
		return;
	}
	if (dm_stat->dm_stat_name)
		free(dm_stat->dm_stat_name);
	if (dm_stat->dm_stat_hard)
		free(dm_stat->dm_stat_hard);
	if (dm_stat->dm_stat_disabled)
		free(dm_stat->dm_stat_disabled);
	if (dm_stat->dm_stat_soft)
		free(dm_stat->dm_stat_soft);
	if (dm_stat->dm_stat_host)
		free(dm_stat->dm_stat_host);
	if (dm_stat->dm_stat_drive)
		free(dm_stat->dm_stat_drive);

	free(dm_stat);
}

mm_dm_stat_t *
mm_get_dm_status(char *dm_name, char *drive_name, char *host, mm_db_t *db) {
	mm_dm_stat_t *dm_status = NULL;

	char		*cmd_buf = NULL;
	/* when dm name is null, use drive and host to return the dm status */
	cmd_buf = mms_strapp(cmd_buf,
		    "select "
		    "\"DM\".\"DMStateHard\", "
		    "\"DM\".\"DMStateSoft\", "
		    "\"DM\".\"DriveName\", "
		    "\"DM\".\"DMDisabled\", "
		    "\"DM\".\"DMTargetHost\", "
		    "\"DM\".\"DMName\" "
		    "from \"DM\" ");

	if (dm_name == NULL) {
		if ((drive_name == NULL) ||
		    (host == NULL)) {
			mms_trace(MMS_ERR,
			    "need drive and host if dm namd is null",
			    ", mm_get_dm_status");
			free(cmd_buf);
			return (NULL);
		}
		/* use drive/host constraint */
		cmd_buf = mms_strapp(cmd_buf,
				"where \"DriveName\" = '%s' "
				"and pg_host_ident(\"DMTargetHost\") "
				"= pg_host_ident('%s');",
				drive_name,
				host);
	} else {
		/* use dm name constrant */
		cmd_buf = mms_strapp(cmd_buf,
				"where \"DM\".\"DMName\" = '%s'",
				dm_name);
	}
	if (mm_db_exec(HERE, db, cmd_buf) != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "db error, mm_get_dm_status");
		free(cmd_buf);
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (NULL);
	}
	free(cmd_buf);
	if (PQntuples(db->mm_db_results) != 1) {
		mms_trace(MMS_ERR,
		    "row num mismatch, "
		    "mm_get_drive_status");
		mm_clear_db(&db->mm_db_results);
		return (NULL);
	}
	dm_status = (mm_dm_stat_t *)calloc(1, sizeof (mm_dm_stat_t));
	if (dm_status == NULL) {
		mms_trace(MMS_ERR,
		    "Unable to malloc dm_status");
		mm_clear_db(&db->mm_db_results);
		return (NULL);
	}

	dm_status->dm_stat_hard = mms_strapp(dm_status->dm_stat_hard,
					PQgetvalue(db->mm_db_results, 0, 0));
	dm_status->dm_stat_soft = mms_strapp(dm_status->dm_stat_soft,
					PQgetvalue(db->mm_db_results, 0, 1));
	dm_status->dm_stat_drive = mms_strapp(dm_status->dm_stat_drive,
					PQgetvalue(db->mm_db_results, 0, 2));
	dm_status->dm_stat_disabled = mms_strapp(dm_status->dm_stat_disabled,
					PQgetvalue(db->mm_db_results, 0, 3));
	dm_status->dm_stat_host = mms_strapp(dm_status->dm_stat_host,
					PQgetvalue(db->mm_db_results, 0, 4));
	dm_status->dm_stat_name = mms_strapp(dm_status->dm_stat_name,
					PQgetvalue(db->mm_db_results, 0, 5));
	mm_clear_db(&db->mm_db_results);
	return (dm_status);

}

void
mm_print_drive_status(mm_drive_stat_t *drive_stat) {
	mms_trace(MMS_DEVP,
	    "Drive Status for Drive, %s",
	    drive_stat->drive_stat_name);
	mms_trace(MMS_DEVP,
	    "    DriveDisabled  = %s",
	    drive_stat->drive_stat_disabled);
	mms_trace(MMS_DEVP,
	    "    DriveBroken = %s",
	    drive_stat->drive_stat_broken);
	mms_trace(MMS_DEVP,
	    "    DriveStateSoft = %s",
	    drive_stat->drive_stat_soft);
	mms_trace(MMS_DEVP,
	    "    DriveStateHard = %s",
	    drive_stat->drive_stat_hard);
	mms_trace(MMS_DEVP,
	    "    DriveLibraryAccessible = %s",
	    drive_stat->drive_stat_lib_acc);
	mms_trace(MMS_DEVP,
	    "    ExclusiveAppName = %s",
	    drive_stat->drive_stat_excl_app);
	mms_trace(MMS_DEVP,
	    "    DriveOnline = %s",
	    drive_stat->drive_stat_online);
	mms_trace(MMS_DEVP,
	    "    DriveGroupName = %s",
	    drive_stat->drive_stat_group);
	mms_trace(MMS_DEVP,
	    "    LibraryName = %s",
	    drive_stat->drive_stat_library);
	mms_trace(MMS_DEVP,
	    "    DrivePriority = %s",
	    drive_stat->drive_stat_priority);
	mms_trace(MMS_DEVP,
	    "    DMName = %s",
	    drive_stat->drive_stat_dm);
	mms_trace(MMS_DEVP,
	    "    DriveGeometry = %s",
	    drive_stat->drive_stat_geometry);
	mms_trace(MMS_DEVP,
	    "    DriveSerialNum = %s",
	    drive_stat->drive_stat_serial);
	mms_trace(MMS_DEVP,
	    "    CartridgePCL = %s",
	    drive_stat->drive_stat_pcl);
	mms_trace(MMS_DEVP,
	    "    DriveLibraryOccupied = %s",
	    drive_stat->drive_stat_drvlib_occ);
}
void
mm_free_drive_status(mm_drive_stat_t *drive_stat) {
	if (drive_stat == NULL) {
		return;
	}
	if (drive_stat->drive_stat_name)
		free(drive_stat->drive_stat_name);
	if (drive_stat->drive_stat_hard)
		free(drive_stat->drive_stat_hard);
	if (drive_stat->drive_stat_disabled)
		free(drive_stat->drive_stat_disabled);
	if (drive_stat->drive_stat_soft)
		free(drive_stat->drive_stat_soft);
	if (drive_stat->drive_stat_library)
		free(drive_stat->drive_stat_library);
	if (drive_stat->drive_stat_lib_acc)
		free(drive_stat->drive_stat_lib_acc);
	if (drive_stat->drive_stat_group)
		free(drive_stat->drive_stat_group);
	if (drive_stat->drive_stat_excl_app)
		free(drive_stat->drive_stat_excl_app);
	if (drive_stat->drive_stat_broken)
		free(drive_stat->drive_stat_broken);
	if (drive_stat->drive_stat_online)
		free(drive_stat->drive_stat_online);
	if (drive_stat->drive_stat_priority)
		free(drive_stat->drive_stat_priority);
	if (drive_stat->drive_stat_dm)
		free(drive_stat->drive_stat_dm);
	if (drive_stat->drive_stat_geometry)
		free(drive_stat->drive_stat_geometry);
	if (drive_stat->drive_stat_serial)
		free(drive_stat->drive_stat_serial);
	if (drive_stat->drive_stat_pcl)
		free(drive_stat->drive_stat_pcl);
	if (drive_stat->drive_stat_drvlib_occ)
		free(drive_stat->drive_stat_drvlib_occ);
	free(drive_stat);
}

mm_drive_stat_t *
mm_get_drive_status(char *drive_name, mm_db_t *db) {
	mm_drive_stat_t *drive_status = NULL;
	if (mm_db_exec(HERE, db,
		    "select "
		    "\"DRIVE\".\"DriveDisabled\", "
		    "\"DRIVE\".\"DriveBroken\", "
		    "\"DRIVE\".\"DriveStateSoft\", "
		    "\"DRIVE\".\"DriveLibraryAccessible\", "
		    "\"DRIVE\".\"ExclusiveAppName\", "
		    "\"DRIVE\".\"DriveOnline\", "
		    "\"DRIVE\".\"DriveGroupName\", "
		    "\"DRIVE\".\"DriveStateHard\", "
		    "\"DRIVE\".\"LibraryName\", "
		    "\"DRIVE\".\"DrivePriority\", "
		    "\"DRIVE\".\"DMName\", "
		    "\"DRIVE\".\"DriveGeometry\", "
		    "\"DRIVE\".\"DriveSerialNum\", "
		    "\"DRIVE\".\"CartridgePCL\", "
		    "\"DRIVE\".\"DriveLibraryOccupied\" "
		    "from \"DRIVE\" "
		    "where \"DriveName\" = '%s';",
		    drive_name) != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "db error, mm_get_drive_status");
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (NULL);
	}
	if (PQntuples(db->mm_db_results) != 1) {
		mms_trace(MMS_ERR,
		    "row num mismatch, "
		    "mm_get_drive_status");
		mm_clear_db(&db->mm_db_results);
		return (NULL);
	}
	drive_status = (mm_drive_stat_t *)calloc(1, sizeof (mm_drive_stat_t));
	if (drive_status == NULL) {
		mms_trace(MMS_ERR,
		    "Unable to malloc drive_status");
		return (NULL);
	}
	drive_status->drive_stat_name =
		mms_strapp(drive_status->drive_stat_name,
		    drive_name);
	drive_status->drive_stat_disabled =
		mms_strapp(drive_status->drive_stat_disabled,
		    PQgetvalue(db->mm_db_results, 0, 0));
	drive_status->drive_stat_broken =
		mms_strapp(drive_status->drive_stat_broken,
		    PQgetvalue(db->mm_db_results, 0, 1));
	drive_status->drive_stat_soft =
		mms_strapp(drive_status->drive_stat_soft,
		    PQgetvalue(db->mm_db_results, 0, 2));
	drive_status->drive_stat_lib_acc =
		mms_strapp(drive_status->drive_stat_lib_acc,
		    PQgetvalue(db->mm_db_results, 0, 3));
	drive_status->drive_stat_excl_app =
		mms_strapp(drive_status->drive_stat_excl_app,
		    PQgetvalue(db->mm_db_results, 0, 4));
	drive_status->drive_stat_online =
		mms_strapp(drive_status->drive_stat_online,
		    PQgetvalue(db->mm_db_results, 0, 5));
	drive_status->drive_stat_group =
		mms_strapp(drive_status->drive_stat_group,
		    PQgetvalue(db->mm_db_results, 0, 6));
	drive_status->drive_stat_hard =
		mms_strapp(drive_status->drive_stat_hard,
		    PQgetvalue(db->mm_db_results, 0, 7));
	drive_status->drive_stat_library =
		mms_strapp(drive_status->drive_stat_library,
		    PQgetvalue(db->mm_db_results, 0, 8));
	drive_status->drive_stat_priority =
		mms_strapp(drive_status->drive_stat_priority,
		    PQgetvalue(db->mm_db_results, 0, 9));
	drive_status->drive_stat_dm =
		mms_strapp(drive_status->drive_stat_dm,
		    PQgetvalue(db->mm_db_results, 0, 10));
	drive_status->drive_stat_geometry =
		mms_strapp(drive_status->drive_stat_geometry,
		    PQgetvalue(db->mm_db_results, 0, 11));
	drive_status->drive_stat_serial =
		mms_strapp(drive_status->drive_stat_serial,
		    PQgetvalue(db->mm_db_results, 0, 12));
	drive_status->drive_stat_pcl =
		mms_strapp(drive_status->drive_stat_pcl,
		    PQgetvalue(db->mm_db_results, 0, 13));
	drive_status->drive_stat_drvlib_occ =
		mms_strapp(drive_status->drive_stat_drvlib_occ,
		    PQgetvalue(db->mm_db_results, 0, 14));

	mm_clear_db(&db->mm_db_results);
	return (drive_status);

}

void
mm_print_lm_status(mm_lm_stat_t *lm_stat) {
	mms_trace(MMS_DEVP,
	    "LM Status for LM, %s",
	    lm_stat->lm_stat_name);
	mms_trace(MMS_DEVP,
	    "    LibraryName = %s",
	    lm_stat->lm_stat_library);
	mms_trace(MMS_DEVP,
	    "    LMStateHard = %s",
	    lm_stat->lm_stat_hard);
	mms_trace(MMS_DEVP,
	    "    LMStateSoft = %s",
	    lm_stat->lm_stat_soft);
	mms_trace(MMS_DEVP,
	    "    LMDisabled = %s",
	    lm_stat->lm_stat_disabled);
}

void
mm_free_lm_status(mm_lm_stat_t *lm_stat) {
	if (lm_stat == NULL) {
		return;
	}
	if (lm_stat->lm_stat_name)
		free(lm_stat->lm_stat_name);
	if (lm_stat->lm_stat_hard)
		free(lm_stat->lm_stat_hard);
	if (lm_stat->lm_stat_disabled)
		free(lm_stat->lm_stat_disabled);
	if (lm_stat->lm_stat_soft)
		free(lm_stat->lm_stat_soft);
	if (lm_stat->lm_stat_library)
		free(lm_stat->lm_stat_library);
	free(lm_stat);
}
mm_lm_stat_t *
mm_get_lm_status(char *lm_name, mm_db_t *db) {
	mm_lm_stat_t *lm_status = NULL;
	if (mm_db_exec(HERE, db,
		"select "
		"\"LM\".\"LMStateHard\", "
		"\"LM\".\"LMStateSoft\", "
		"\"LM\".\"LMDisabled\", "
		"\"LM\".\"LibraryName\" "
		"from \"LM\" "
		"where \"LMName\" = '%s';",
		lm_name) != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "db error, mm_get_lm_status");
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (NULL);
	}
	if (PQntuples(db->mm_db_results) != 1) {
		mms_trace(MMS_ERR,
		    "row num mismatch, "
		    "mm_get_lm_status");
		mm_clear_db(&db->mm_db_results);
		return (NULL);
	}
	lm_status = (mm_lm_stat_t *)calloc(1, sizeof (mm_lm_stat_t));
	if (lm_status == NULL) {
		mms_trace(MMS_ERR,
		    "Unable to malloc lm_status");
		return (NULL);
	}
	lm_status->lm_stat_name =
		mms_strapp(lm_status->lm_stat_name,
		    lm_name);
	lm_status->lm_stat_hard =
		mms_strapp(lm_status->lm_stat_hard,
		    PQgetvalue(db->mm_db_results, 0, 0));
	lm_status->lm_stat_soft =
		mms_strapp(lm_status->lm_stat_soft,
		    PQgetvalue(db->mm_db_results, 0, 1));
	lm_status->lm_stat_disabled =
		mms_strapp(lm_status->lm_stat_disabled,
		    PQgetvalue(db->mm_db_results, 0, 2));
	lm_status->lm_stat_library =
		mms_strapp(lm_status->lm_stat_library,
		    PQgetvalue(db->mm_db_results, 0, 3));
	mm_clear_db(&db->mm_db_results);
	return (lm_status);
}
void
mm_print_library_status(mm_lib_stat_t *lib_stat) {
	mms_trace(MMS_DEVP,
	    "Library Status for Library, %s",
	    lib_stat->lib_stat_name);
	mms_trace(MMS_DEVP,
	    "    LibraryOnline = %s",
	    lib_stat->lib_stat_online);
	mms_trace(MMS_DEVP,
	    "    LibraryDisabled = %s",
	    lib_stat->lib_stat_disabled);
	mms_trace(MMS_DEVP,
	    "    LibraryBroken = %s",
	    lib_stat->lib_stat_broken);
	mms_trace(MMS_DEVP,
	    "    LMName = %s",
	    lib_stat->lib_stat_lm);
}


void
mm_free_library_status(mm_lib_stat_t *lib_stat) {
	if (lib_stat == NULL) {
		return;
	}
	if (lib_stat->lib_stat_name)
		free(lib_stat->lib_stat_name);
	if (lib_stat->lib_stat_online)
		free(lib_stat->lib_stat_online);
	if (lib_stat->lib_stat_disabled)
		free(lib_stat->lib_stat_disabled);
	if (lib_stat->lib_stat_broken)
		free(lib_stat->lib_stat_broken);
	if (lib_stat->lib_stat_lm)
		free(lib_stat->lib_stat_lm);
	free(lib_stat);
}
mm_lib_stat_t *
mm_get_library_status(char *library_name, mm_db_t *db) {
	mm_lib_stat_t *library_status = NULL;

	if (mm_db_exec(HERE, db,
		"select "
		"\"LIBRARY\".\"LibraryOnline\", "
		"\"LIBRARY\".\"LibraryDisabled\", "
		"\"LIBRARY\".\"LibraryBroken\", "
		"\"LIBRARY\".\"LMName\" "
		"from \"LIBRARY\" "
		"where \"LibraryName\" = '%s';",
		library_name) != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "db error, mm_get_library_status");
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (NULL);
	}
	if (PQntuples(db->mm_db_results) != 1) {
		mms_trace(MMS_ERR,
		    "row num mismatch, "
		    "mm_get_library_status");
		mm_clear_db(&db->mm_db_results);
		return (NULL);
	}

	library_status = (mm_lib_stat_t *)calloc(1, sizeof (mm_lib_stat_t));
	if (library_status == NULL) {
		mms_trace(MMS_ERR,
		    "Unable to malloc library_status");
		return (NULL);
	}
	library_status->lib_stat_name =
		mms_strapp(library_status->lib_stat_name,
		    library_name);
	library_status->lib_stat_online =
		mms_strapp(library_status->lib_stat_online,
		    PQgetvalue(db->mm_db_results, 0, 0));
	library_status->lib_stat_disabled =
		mms_strapp(library_status->lib_stat_disabled,
		    PQgetvalue(db->mm_db_results, 0, 1));
	library_status->lib_stat_broken =
		mms_strapp(library_status->lib_stat_broken,
		    PQgetvalue(db->mm_db_results, 0, 2));
	library_status->lib_stat_lm =
		mms_strapp(library_status->lib_stat_lm,
		    PQgetvalue(db->mm_db_results, 0, 3));
	mm_clear_db(&db->mm_db_results);
	return (library_status);
}


static char *
mm_sql_get_ops(char *op)
{
	int		x;

	for (x = 0; x < num_ops; x++) {
		if (strcmp(opstab[x].sql_mmp_ops, op) == 0) {
			return (opstab[x].sql_ops);
		}
	}
	/* Not in table */
	return ("??UNKNOWN??");
}

int
mm_sql_chk_len(char **line, int off, int *bufsize, int len)
{
	char		*new;
	int		 new_bufsize;


	if (off + len < *bufsize) {
		return (0);
	}
	while (off + len > *bufsize) {
		new_bufsize = *bufsize + SQL_CMD_BUF_INCR;
		new = realloc(*line, new_bufsize);
		if (new == NULL) {
			return (-1);
		}
		*line = new;
		*bufsize = new_bufsize;
	}
	return (0);
}


int
mm_notify_delete(mm_db_t *db, mm_command_t *cmd, char *objname,
	int match_off)
{
	PGresult	*results;

	int		rows;
	int		row;


	mms_trace(MMS_DEVP, "mm_notify_delete, object %s", objname);

	/* get object instance */
	if (mm_db_exec(HERE, db, "SELECT \"%sName\", \"%sTargetHost\""
	    " FROM \"%s\" %s", objname, objname,
	    objname, &cmd->cmd_buf[match_off]) != MM_DB_DATA) {
		mm_sql_db_err_rsp_new(cmd, db);
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (MM_CMD_ERROR);
	}

	results = db->mm_db_results;
	rows = PQntuples(results);

	if (rows == 0) {
		mms_trace(MMS_DEVP,
		    "Didn't match any %s's for delete", objname);
		mm_clear_db(&db->mm_db_results);
		return (INTRP_OK);
	}
	for (row = 0; row < rows; row ++) {
		mms_trace(MMS_DEVP,
		    "notify object %s instance %s",
		    objname,
		    PQgetvalue(results, row, 0));

		if (mm_notify_add_config(cmd->wka_ptr, cmd,
		    EVENT_CFG_DELETE,
		    objname,
		    PQgetvalue(results, row, 0),
		    PQgetvalue(results, row, 1))) {
			mms_trace(MMS_ERR,
			    "mm_notify_delete: "
			    "error adding config event");
		}

	}

	mm_clear_db(&db->mm_db_results);
	return (INTRP_OK);

no_mem:
	MM_ABORT_NO_MEM();
	return (MM_CMD_ERROR);

}



int
mm_sql_notify_inst(mm_db_t *db, mm_command_t *cmd, char *objname,
	int match_off, char **objinst)
{
	PGresult	*results = db->mm_db_results;
	char		*value;



	mms_trace(MMS_DEVP, "sql notify inst, object %s", objname);

	*objinst = NULL;

	/* get object instance */
	if (mm_db_exec(HERE, db, "SELECT \"%sName\" FROM \"%s\" %s", objname,
	    objname, &cmd->cmd_buf[match_off]) != MM_DB_DATA) {
		mm_sql_db_err_rsp_new(cmd, db);
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		db->mm_db_results = results;
		return (MM_CMD_ERROR);
	}

	if (PQntuples(db->mm_db_results) != 1) {
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = results;
		return (INTRP_OK);
	}
	value = PQgetvalue(db->mm_db_results, 0, 0);
	if (value[0] == '\0') {
		*objinst = strdup("none"); /* none used for empty string */
	} else {
		*objinst = strdup(value);
	}
	mm_clear_db(&db->mm_db_results);
	db->mm_db_results = results;
	if (objinst == NULL) {
		MM_ABORT("object instance");
		return (MM_CMD_ERROR);
	}
	mms_trace(MMS_DEVP, "notify object %s instance %s", objname, *objinst);
	return (INTRP_OK);

no_mem:
	MM_ABORT_NO_MEM();
	return (MM_CMD_ERROR);

}

void
mm_sql_report_write_value(char **buf, PGresult *results,
			int row, int col, mm_command_t *cmd,
			int reportmode) {

	mm_db_t		*db = &cmd->wka_ptr->mm_data->mm_db;

	/* 3 possible reportmode formatting */
	int			name_rt = 1;
	int			value_rt = 2;
	int			namevalue_rt = 3;

	Oid		 oid;
	char		*value;
	char		*name;

	char		 date[24];
	int		 i;
	int		 len;

	name = PQfname(results, col);
	value = PQgetvalue(results, row, col);
	oid = PQftype(results, col);

	/* database data types to mms string conversions */
	oid = PQftype(results, col);
	if (oid == db->mm_db_cfg->mm_db_bool_oid) {
		if (strcmp(value, "t") == 0) {
			value = "true";
		} else if (strcmp(value, "f") == 0) {
			value = "false";
		}
	} else if (oid == db->mm_db_cfg->mm_db_timestamp_oid) {
		if (strcmp(value, "-infinity") == 0) {
			/* mms time not set */
			value = "0000 00 00 00 00 00 000";
		} else {
			strcpy(date, value);
			value = date;
			/* mms UTC time format */
			value[4] = ' '; /* - */
			value[7] = ' '; /* - */
			value[13] = ' '; /* : */
			value[16] = ' '; /* : */
			if (value[19] == '\0') {
				value[20] = '\0';
			}
			value[19] = ' '; /* . */


			len = strlen(value);
			for (i = len; i < 23; i++) {
				value[i] = '0';
			}
			value[23] = '\0';
		}
	}
	if ((reportmode == name_rt) ||
	    (reportmode == namevalue_rt)) {
		/* add name */
		*(buf) = mms_strapp(*(buf),
				" \"%s\"",
				name);
	}
	if ((reportmode == value_rt) ||
	    (reportmode == namevalue_rt)) {
		*(buf) = mms_strapp(*(buf), " \"%s\"", value);
	}
	return;

}


char *
mm_sql_report_attr(mm_command_t *cmd,
		int row, int *col_count,
		int num_atts, int reportmode,
		PGresult *results) {

	int		col = *(col_count);
	int		start_col = *(col_count);

	char		*attr_buf = NULL;
	int			print = 0;

	if (print)
		mms_trace(MMS_DEVP,
		    "create attlist clause for row %d",
		    row);
	/* print the attr list for row starting with col_count */
	attr_buf = mms_strapp(attr_buf,
			"attrlist[");
	/* LINTED: */
	for (col; col < (start_col + num_atts); col ++) {
			mm_sql_report_write_value(&attr_buf,
						results, row,
						col, cmd,
						reportmode);
	}
	attr_buf = mms_strapp(attr_buf,
			"]");
	*(col_count) = col;
	return (attr_buf);

}

char *
mm_sql_report_text_row(mm_command_t *cmd,
		    int row, PGresult *results, int reportmode,
		    mms_list_t *format) {

	mm_char_list_t *cur;
	mm_char_list_t *next;

	char			*text_buf = NULL;
	char			*attr_buf = NULL;
	int			col_count = 0;

	int			print = 0;

	/* Create the text clause for the report in cmd */
	if (print)
		mms_trace(MMS_DEVP,
		    "create text clause for row %d",
		    row);

	/* First bracket for this text clause */
	text_buf = mms_strapp(text_buf, "text[");
	for (cur = mms_list_head(format);
	    cur != NULL;
	    cur = next) {
		next = mms_list_next(format, cur);
		if (cur->number == 0) {
			/* Do data base format conversion */
			/* The function will write into text buf */
			mm_sql_report_write_value(&text_buf,
						results, row,
						col_count, cmd,
						reportmode);
			col_count ++;
		} else {
			/* This is an attrlist clause */
			if ((attr_buf = mm_sql_report_attr(cmd,
							row, &col_count,
							cur->number, reportmode,
							results)) == NULL) {
				mms_trace(MMS_ERR,
				    "error setting atr_buf");
				if (text_buf)
					free(text_buf);
				return (NULL);
			}
			text_buf = mms_strapp(text_buf, "%s",
					attr_buf);
			free(attr_buf);


		}
	}

	/* Trailing bracket for this text text */
	text_buf = mms_strapp(text_buf, "]");

	return (text_buf);


}



int
mm_sql_format_report(mm_command_t *cmd, mms_par_node_t *report,
		mms_list_t *format) {

	mms_par_node_t		*report_work;
	mms_par_node_t		*report_work_next;
	mms_par_node_t		*object;
	mms_par_node_t		*object_next;


	/* 2 possible data types in report */
	int			data_type;
	int			obj_att = 1;
	int			att_list = 2;

	char			*cur_obj;
	char			*cur_attr;

	int			print = 0;
	int			num_atts = 0;
	mm_db_t		*db = &cmd->wka_ptr->mm_data->mm_db;

	/* Create the text clause for the report in cmd */
	if (print)
		mms_trace(MMS_DEVP,
		    "determine formatting for this report");

	data_type = 0;
	/* Need to use attr list for OBJECTS */
	report_work = NULL;
	for (object = mms_pn_lookup(report, NULL, NULL, &report_work);
	    object != NULL;
	    object = mms_pn_lookup(report, NULL, NULL, &report_work)) {
		if (object->pn_string != NULL) {
			if (print)
				mms_trace(MMS_DEVP,
				    "cur object is %s",
				    object->pn_string);
		}

		if (object->pn_type == MMS_PN_OBJ) {
			/* determine if attr list is needed */
			report_work_next = NULL;
			report_work_next = report_work;
			object_next = NULL;
			object_next =
				mms_pn_lookup(report, NULL, NULL,
						&report_work_next);

			if ((object_next != NULL) &&
			    (object_next->pn_type == MMS_PN_ATTR)) {
				/* don't need an attrlist */
				/* set the obj name */
				/* The next object will be an attribute */
				if (print)
					mms_trace(MMS_DEVP,
					    "set obj for obj.attr, %s",
					    object->pn_string);
				cur_obj = object->pn_string;
			} else {
				/* Need attr list */
				/* Write all name/values for this obj */
				if (print)
					mms_trace(MMS_DEVP,
					    "set obj for attr list, %s",
					    object->pn_string);
				cur_obj = object->pn_string;
				data_type = att_list;
			}
		}
		if (object->pn_type == MMS_PN_ATTR) {
			/* Write the name/value for this obj.att */
			if (print)
				mms_trace(MMS_DEVP,
				    "set attr for obj.attr, %s",
				    object->pn_string);
			cur_attr = object->pn_string;
			data_type = obj_att;
		}

		if (data_type == obj_att) {
			data_type = 0;
			/* This is an obj_att */
			(void) mm_add_int(0, format);
			if (print)
				mms_trace(MMS_DEVP,
				    "add 0 to int list %s.\"%s\"",
				    cur_obj, cur_attr);

		}
		if (data_type == att_list) {
			data_type = 0;
			/* This is an attr list */
			/* Determine the number of attributes */
			if (print)
				mms_trace(MMS_DEVP,
				    "set attr num for %s",
				    cur_obj);

			if (mm_db_exec(HERE, db,
				    "select * from \"%s\" limit 1;",
				    cur_obj) != MM_DB_DATA) {
				mms_trace(MMS_ERR,
				    "db error getting attribute number");
				mm_clear_db(&db->mm_db_results);
				db->mm_db_results = NULL;
				return (1);
			}
			num_atts = PQnfields(db->mm_db_results);
			if (print)
				mms_trace(MMS_DEVP,
				    "attr num is %d for %s",
				    num_atts,
				    cur_obj);
			(void) mm_add_int(num_atts, format);
			mm_clear_db(&db->mm_db_results);

		}



	}

	/* done */
	return (0);



}
int
mm_check_cmd_size(int header_length, char *text_clause, char *text_buf) {

	int buf_len;
	int clause_len;
	int char_size = sizeof (char);

	if (text_clause == NULL) {
		/* is the 1st text clause, this must fit */
		return (0);
	}
	if (text_buf == NULL) {
		return (0);
	}

	buf_len = strlen(text_buf);
	clause_len = strlen(text_clause);

	if ((char_size * (header_length + buf_len + clause_len)) >
	    MM_CMD_SIZE_LIMIT) {
		mms_trace(MMS_DEVP,
		    "header len == %d, buf len == %d, "
		    "clause len == %d, total == %d, "
		    "LIMIT == %d",
		    header_length, buf_len, clause_len,
		    header_length + buf_len + clause_len,
		    MM_CMD_SIZE_LIMIT);
		return (1);
	} else {
		return (0);
	}
}

static int
mm_sql_report(mm_command_t *cmd)
{
	mm_db_t			*db = &cmd->wka_ptr->mm_data->mm_db;
	mms_par_node_t		*report;
	mms_par_node_t		*reportmode;
	PGresult		*results;

	int			report_type = 2;
	int			name_rt = 1;
	int			namevalue_rt = 3;

	/* buf for whole response */
	char			*resp_buf = NULL;
	/* buf for multiple text clauses */
	char			*text_buf = NULL;
	/* buf for a single text clause */
	char			*text_clause = NULL;

	int			number_of_rows = 0;
	int			row;

	/* Report format */
	mms_list_t			format;

	/* CMD size limit */
	int			header_length;


	mms_trace(MMS_DEVP, "sql report");

	mms_list_create(&format, sizeof (mm_char_list_t),
	    offsetof(mm_char_list_t, mm_char_list_next));

	if (mm_db_exec(HERE, db, "%s;", cmd->cmd_buf) != MM_DB_DATA) {
		mm_sql_db_err_rsp_new(cmd, db);
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (1);
	}
	if (PQresultStatus(db->mm_db_results) != PGRES_TUPLES_OK) {
		SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize,
		    strlen(ECLASS_INTERNAL) + strlen(EDATABASE) + 1);
		(void) snprintf(cmd->cmd_buf, cmd->cmd_bufsize,
		    RESPONSE_ERROR, cmd->cmd_task,
		    ECLASS_INTERNAL, EDATABASE);
		return (1);
	}

	number_of_rows = PQntuples(db->mm_db_results);
	mms_trace(MMS_DEVP, "number of rows is %d", number_of_rows);

	/* Only return info user wants. The default is value only. */
	reportmode = mms_pn_lookup(cmd->cmd_root, "reportmode",
	    MMS_PN_CLAUSE, NULL);

	if (reportmode && mms_pn_lookup(reportmode, "name",
	    MMS_PN_KEYWORD, NULL)) {
		report_type = name_rt;
	} else if (reportmode && mms_pn_lookup(reportmode, "namevalue",
	    MMS_PN_KEYWORD, NULL)) {
		report_type = namevalue_rt;
	} else if (reportmode && mms_pn_lookup(reportmode, "number",
	    MMS_PN_KEYWORD, NULL)) {
		/* Generate a the number report and return */
		text_buf = mms_strapp(text_buf,
		    "text[\"%d\"]",
		    number_of_rows);
		if (cmd->cmd_buf != NULL) {
			free(cmd->cmd_buf);
			cmd->cmd_buf = NULL;
		}
		cmd->cmd_buf = mms_strapp(cmd->cmd_buf,
		    RESPONSE_SUCCESS_STR,
		    cmd->cmd_task,
		    text_buf);
		free(text_buf);
		mm_clear_db(&db->mm_db_results);
		mms_trace(MMS_DEVP, "\n\n%s\n", cmd->cmd_buf);
		return (0);

	}

	/* Build the text report */
	/* there will be a text clause for each of the rows */
	results = db->mm_db_results;

	report = mms_pn_lookup(cmd->cmd_root, "report",
	    MMS_PN_CLAUSE, NULL);
	if (report == NULL) {
		/* no report clause */
		/* there will be no text clause in response */
		number_of_rows = 0;
	} else {
		/* Determine the formatting for the entire report */
		if (mm_sql_format_report(cmd, report, &format)) {
			mms_trace(MMS_ERR,
			    "error determineing report format");
			mm_system_error(cmd,
			    "error determineing report format");
			mm_clear_db(&results);
			return (1);
		}
	}
	if (number_of_rows != 0) {
		/* There will be text clauses */
		header_length = strlen(RESPONSE_INTERMEDIATE) +
		    strlen(cmd->cmd_task);
		mms_trace(MMS_DEVP,
		    "response header size is %d",
		    header_length);
	}

	for (row = 0; row < number_of_rows; row++) {
		text_clause = NULL;

		if ((text_clause = mm_sql_report_text_row(cmd, row, results,
		    report_type,
		    &format)) == NULL) {
			mms_trace(MMS_ERR,
			    "error generating text clause");
			mm_system_error(cmd,
			    "error generating text clause");
			mm_clear_db(&results);
			return (1);

		}

		/* Check the MM_CMD_SIZE_LIMIT */
		/* before appending the next text clause */
		/* this command may need intermediate packets */
		if (mm_check_cmd_size(header_length, text_clause, text_buf)) {

			mm_char_list_t *node;

			/* This text clause will push response over limit */
			mms_trace(MMS_DEVP,
			    "command size over the limit");
			/* add the curr text buf to the response list */
			if (resp_buf)
				free(resp_buf);
			resp_buf = NULL;
			resp_buf = mms_strapp(resp_buf, RESPONSE_INTERMEDIATE,
			    cmd->cmd_task,
			    text_buf);

			node =
			    (mm_char_list_t *)malloc(sizeof (mm_char_list_t));
			if (node == NULL) {
				mms_trace(MMS_ERR,
				    "Error malloc response object");
				mm_clear_db(&results);
				return (1);
			} else {
				memset(node, 0, sizeof (mm_char_list_t));
				node->text = NULL;
				node->text = resp_buf;
				mms_list_insert_tail(&cmd->cmd_resp_list, node);
				resp_buf = NULL;
			}

			if (text_buf)
				free(text_buf);
			text_buf = NULL;
		}
		text_buf = mms_strapp(text_buf,
		    text_clause);
		if (text_clause)
			free(text_clause);
		text_clause = NULL;

	}

	/* clean up formatting */
	mm_free_list(&format);
	mms_list_destroy(&format);

	if (number_of_rows == 0) {
		mm_char_list_t	*node;

		/* add the curr text buf to the response list */
		if (resp_buf)
			free(resp_buf);
		resp_buf = NULL;
		resp_buf = mms_strapp(resp_buf, RESPONSE_SUCCESS,
		    cmd->cmd_task);

		node =
		    (mm_char_list_t *)malloc(sizeof (mm_char_list_t));
		if (node == NULL) {
			mms_trace(MMS_ERR, "Error malloc response object");
			mm_clear_db(&results);
			return (1);
		} else {
			memset(node, 0, sizeof (mm_char_list_t));
			node->text = NULL;
			node->text = resp_buf;
			mms_list_insert_tail(&cmd->cmd_resp_list, node);
			resp_buf = NULL;
		}

		/* Set the success response  with no text */
		SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize,
		    strlen(RESPONSE_SUCCESS) +
		    strlen(cmd->cmd_task) + 1);
		(void) snprintf(cmd->cmd_buf, cmd->cmd_bufsize,
		    RESPONSE_SUCCESS, cmd->cmd_task);
	} else {
		mm_char_list_t *node;
		/* add the curr text buf to the response list */
		if (resp_buf)
			free(resp_buf);
		resp_buf = NULL;
		resp_buf = mms_strapp(resp_buf, RESPONSE_SUCCESS_STR,
		    cmd->cmd_task,
		    text_buf);

		node = (mm_char_list_t *)malloc(sizeof (mm_char_list_t));
		if (node == NULL) {
			mms_trace(MMS_ERR, "Error malloc response object");
			mm_clear_db(&results);
			return (1);
		} else {
			memset(node, 0, sizeof (mm_char_list_t));
			node->text = NULL;
			node->text = resp_buf;
			mms_list_insert_tail(&cmd->cmd_resp_list, node);
			resp_buf = NULL;
		}

		/* Set the success response  with a text */
		SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize,
		    strlen(RESPONSE_SUCCESS_STR) + strlen(cmd->cmd_task) +
		    strlen(text_buf) + 1);
		(void) snprintf(cmd->cmd_buf, cmd->cmd_bufsize,
		    RESPONSE_SUCCESS_STR, cmd->cmd_task, text_buf);
	}

	if (text_buf)
		free(text_buf);
	mm_clear_db(&results);
	return (0);

no_mem:
	MM_ABORT_NO_MEM();
	return (1);

}


static void
mm_sql_trans_volname(mm_command_t *cmd, int *offset)
{
	mms_par_node_t	*volname;
	mms_par_node_t	*arg;
	int		 off = *offset;


	mms_trace(MMS_DEVP, "sql trans volname");
	MMS_PN_LOOKUP(volname, cmd->cmd_root, "volname",
	    MMS_PN_CLAUSE, NULL);

	SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize, 1);
	off += snprintf(cmd->cmd_buf + off, cmd->cmd_bufsize - off, "(");

	mms_list_foreach(&volname->pn_arglist, arg) {
		SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
		    28 + strlen(arg->pn_string));
		off += snprintf(cmd->cmd_buf + off, cmd->cmd_bufsize - off,
		    "(\"VOLUME\".\"VolumeName\" = '%s')",
		    arg->pn_string);
		if (arg != mms_list_tail(&volname->pn_arglist)) {
			SQL_CHK_LEN(&cmd->cmd_buf, off,
			    &cmd->cmd_bufsize, 4);
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off, " OR ");
		}
	}
	SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize, 1);
	off += snprintf(cmd->cmd_buf + off, cmd->cmd_bufsize - off, ")");

	*offset = off;
	return;

no_mem:
	MM_ABORT_NO_MEM();
	return;

not_found:
	mm_response_error(cmd,
	    ECLASS_LANGUAGE,
	    ENOTFOUND,
	    MM_5062_MSG,
	    NULL);
	return;

}

int
mm_make_constraint_helper(mms_par_node_t *node, mm_command_t *cmd,
			    int num, char **buf, int *offset,
			    int *bufsize, int current, int reg_ex) {

	mms_par_node_t	*arg;
	mms_par_node_t	*next_arg;
	int		 off = *offset;
	int		b_size = *bufsize;
	char		*sql_ops;
	const char	*att_test;
	char 		*att_cond;
	char		*obj;
	char		*att;
	int cur = current;
	int regex = reg_ex;
	int twice = 0;



	mms_trace(MMS_DEVP, "constraint helper -> num -> %d "	\
	    " current -> %d", num, current);

	if (node->pn_string != NULL) {
		mms_trace(MMS_DEVP, "1 Object is %s", node->pn_string);
		if (strcmp(node->pn_string, "regex") ==  0) {
			regex = 1;
		}
	}

	if (*buf != NULL) {
		mms_trace(MMS_DEVP, "1)%s", *buf);

	}


	if (node->pn_type & MMS_PN_STRING) {
		if (cur == num) {

		/* Could be numeric or null string or string. */
		if (node->pn_type == MMS_PN_NUMERIC) {

			SQL_CHK_LEN(buf, off, &b_size,
				    1 + strlen(node->pn_string));
			off += snprintf(*buf + off, b_size - off,
				    "%s", node->pn_string);


		} else if (node->pn_type & MMS_PN_NULLSTR) {
		    if (strcmp(*buf + off - 3, " = ") == 0) {

			off -= 2; /* backup over operator */
			SQL_CHK_LEN(buf, off, &b_size, 7);
			off += snprintf(*buf + off, b_size - off, "ISNULL");

		    } else if (strcmp(*buf + off - 4, " <= ") == 0 ||
				strcmp(*buf + off - 4, " >= ") == 0) {

			off -= 3; /* backup over operator */
			SQL_CHK_LEN(buf, off, &b_size, 7);
			off += snprintf(*buf + off, b_size - off, "ISNULL");

		    } else if (strcmp(*buf + off - 4, " <> ") == 0) {

			off -= 3; /* backup over operator */
			SQL_CHK_LEN(buf, off, &b_size, 8);
			off += snprintf(*buf + off, b_size - off, "NOTNULL");
		    } else {
			SQL_CHK_LEN(buf, off, &b_size,
				    1 + strlen(node->pn_string));
			off += snprintf(*buf + off, b_size - off,
				    "%s", node->pn_string);
		    }
		} else {
			SQL_CHK_LEN(buf, off, &b_size,
				    3 + strlen(node->pn_string));
			off += snprintf(*buf + off, b_size - off,
				    "'%s'", node->pn_string);
		}
		}
		if (!regex)
			cur ++;
			/* Just Worte a constraint value */
	}


	if (node->pn_type == MMS_PN_OBJ) {

		/* An object-attribute */
		twice = 0;
	if (cur == num) {
		if (*buf != NULL) {
			twice = 1;
		}
		SQL_CHK_LEN(buf, off, &b_size,
			    4 + strlen(node->pn_string));
		off += snprintf(*buf + off, b_size - off,
			    "\"%s\".", node->pn_string);
	}
		arg = mms_list_head(&node->pn_arglist);
	if (cur == num) {
		SQL_CHK_LEN(buf, off, &b_size,
			    3 + strlen(arg->pn_string));
		off += snprintf(*buf + off, b_size - off,
			    "\"%s\"", arg->pn_string);
		if (regex)
			cur ++;
		if (twice)
			cur ++;
	}

	} else if (node->pn_flags & MMS_PN_MULTIOPS) {

		/*
		 * A multiops has two or more args hanging off the arglist.
		 * Insert the ops between the args by adding an arg, then add
		 * the ops and repeat until the last arg.
		 */
		if (node->pn_flags & MMS_PN_REGEX) {

			/* sql regular expression is at end of list */
			arg = mms_list_head(&node->pn_arglist);
			mms_list_remove(&node->pn_arglist, arg);
			mms_list_insert_tail(&node->pn_arglist, arg);
		}
		mms_list_foreach(&node->pn_arglist, arg) {
			cur = mm_make_constraint_helper(arg, cmd,
							num, buf, &off,
							&b_size, cur, regex);

			if (arg != mms_list_tail(&node->pn_arglist)) {

				sql_ops = mm_sql_get_ops(node->pn_string);
				if ((strcmp(sql_ops, "AND") == 0) ||
				    (strcmp(sql_ops, "OR") == 0) ||
				    (strcmp(sql_ops, "NOT") == 0) ||
				    (strcmp(sql_ops, "ASC") == 0) ||
				    /* LINTED: dont change */
				    (strcmp(sql_ops, "DESC") == 0)) {
				} else {
					if (cur == num) {
				SQL_CHK_LEN(buf, off,
					    &b_size,
					    3 + strlen(sql_ops));
				off += snprintf(*buf + off, b_size - off,
					    " %s ", sql_ops);
					}
				}
			}

		}
		if (node->pn_flags & MMS_PN_REGEX) {
			/* restore list to original order */
			arg = mms_list_tail(&node->pn_arglist);
			mms_list_remove(&node->pn_arglist, arg);
			mms_list_insert_head(&node->pn_arglist, arg);
		}
	} else if (node->pn_flags & MMS_PN_UNARYOPS) {

		/* Unary ops has only one arg */
		if (strcmp(node->pn_string, "isattr") == 0 ||
		    strcmp(node->pn_string, "noattr") == 0) {

			att_test = "mm_obj_has_att('%s','%s') = '%s'";

			if (strcmp(node->pn_string, "isattr") == 0) {
				att_cond = "true";
			} else {
				att_cond = "false";
			}

			arg = mms_list_head(&node->pn_arglist);
			obj = arg->pn_string;

			next_arg = mms_list_head(&arg->pn_arglist);
			att = next_arg->pn_string;
	if (cur == num) {
			SQL_CHK_LEN(buf, off, &b_size,
				strlen(att_test) + 1);
			off += snprintf(*buf + off, b_size - off,
				att_test, obj, att, att_cond);
	}
		} else {
			sql_ops = mm_sql_get_ops(node->pn_string);
	if (cur == num) {
			SQL_CHK_LEN(buf, off, &b_size,
				    strlen(sql_ops) + 3);
			off += snprintf(*buf + off, b_size - off,
				    " %s ", sql_ops);
	}
			arg = mms_list_head(&node->pn_arglist);
			cur = mm_make_constraint_helper(arg, cmd,
							num, buf, &off,
							&b_size, cur, regex);
		}
	}
	*offset = off;
	*bufsize = b_size;

	return (cur);

no_mem:
	MM_ABORT_NO_MEM();
	return (MM_CMD_ERROR);

not_found:

	return (MM_CMD_ERROR);
}

char *
mm_make_constraint(mm_command_t *cmd, int num) {
	char *object;
	char *buf = NULL;
	mms_par_node_t	*match;
	mms_par_node_t	*node;
	int off = 0;
	int bufsize = 0;
	int cur = 0;


	mms_trace(MMS_DEBUG, "mm_make_constraint");

	object = (char *)mm_return_char(&cmd->cmd_dest_list, num);


	match = mms_pn_lookup(cmd->cmd_root, "match",
				MMS_PN_CLAUSE, NULL);
	if (match == NULL) {
		return (NULL);
	}


	mms_list_foreach(&match->pn_arglist, node) {

		mms_trace(MMS_DEVP, "examing a node...");

		off = 0;
		bufsize = 0;
		cur = mm_make_constraint_helper(node, cmd, num, &buf, &off,
						&bufsize, cur, 0);

		mms_trace(MMS_DEVP, "After helper...");

	}
	if (buf != NULL) {
		mms_trace(MMS_DEVP, "Final Constraint for %s -> %s",
		    object, buf);
	} else {
		mms_trace(MMS_DEVP, "Final for %s is NULL",
		    object);
		return (NULL);
	}

	return (buf);
}
int
mm_use_and_or(mm_command_t *cmd) {
	/*
	 * return 1 if the operator is 'and'
	 * between constraints with indexes 'idx1' and 'idx2'
	 * return 0 if the operator is 'or'
	 */
	mms_par_node_t	*match;
	mms_par_node_t	*node;
	int oper = 1;

	match = mms_pn_lookup(cmd->cmd_root, "match",
				MMS_PN_CLAUSE, NULL);
	if (match == NULL) {
		return (NULL);
	}


	mms_list_foreach(&match->pn_arglist, node) {

		mms_trace(MMS_DEVP, "examing a node...");
		if (node->pn_string != NULL) {
			mms_trace(MMS_DEVP, " Node is %s", node->pn_string);
			if (strcmp(node->pn_string, "and") ==  0) {
				oper = 1;
			}
			if (strcmp(node->pn_string, "or") ==  0) {
				oper = 0;
			}
		}

	}
	return (oper);
}

int
mm_sql_report_func(mm_command_t *cmd, mm_db_t *db) {
	char *buf = NULL;
	mm_path_t *path = NULL;
	char *joined[100];
	int joined_count = 0;
	int skip = 0;
	int wrote_one = 0;

	char *source_buf;
	char *dest_buf;
	char *savepoint = NULL;

	int print_message = 0;
	int i;
	int y;
	int j;
	int k;
	int x;
	int l;

	mms_trace(MMS_DEBUG, "mm_sql_make_sql_func");

	for (y = 0; y < 100; y ++) {
		joined[y] = NULL;
	}

	/* create report functins for each report onject */
	for (i = 0; i < cmd->cmd_source_num; i ++) { /* source */
		joined_count = 0;
		/* Make report_n() for this source */

		source_buf =
			(char *)mm_return_char(&cmd->cmd_source_list, i);
		buf = mms_strapp(buf,
			    "CREATE OR REPLACE FUNCTION "\
			    "report_%d_%d() RETURNS SETOF \"%s\" AS $$\n",
			    i, db->mm_db_fd, source_buf);
		buf = mms_strapp(buf,
			    "select distinct \"%s\".* from \"%s\" ",
			    source_buf,
			    source_buf);
		joined_count = 0;
		joined[0] = NULL;
		joined[0] = mms_strapp(joined[0], source_buf);


		joined_count ++;
		for (j = 0; j < cmd->cmd_dest_num; j ++) {
			dest_buf =
				(char *)mm_return_char(&cmd->cmd_dest_list, j);

			if (print_message) {
				mms_trace(MMS_DEVP, "    1) Dest %d -> %s",
				    j, dest_buf);

				mms_trace(MMS_DEVP, "mm_get_path(%s, %s)",
				    dest_buf,
				    source_buf);
			}


			if ((path = mm_get_path(dest_buf,
						source_buf)) == NULL) {
				mms_trace(MMS_DEVP, "Path is NULL, %s to %s",
				    dest_buf,
				    source_buf);

			} else {
			for (k = 0; k < path->mm_node_num; k++) {
				skip = 0;
				for (x = 0; x < joined_count; x++) {

					if (strcmp(joined[x],
					path->mm_node[k]->mm_obj) == 0) {
						/* same so skip */
						skip = 1;
					}
				}
				if (!skip) {
					buf = mms_strapp(buf,
						    "\ncross join \"%s\" ",
						    path->mm_node[k]->mm_obj);

					joined[joined_count] = NULL;
					joined[joined_count] =
						mms_strapp(joined[joined_count],
						path->mm_node[k]->mm_obj);
					joined_count ++;
				}
			}
			}

		}

		if (cmd->cmd_dest_num == 0) {
			buf = mms_strapp(buf,
				    ";\n");
		}

		for (j = 0; j < cmd->cmd_dest_num; j ++) {
			/* j cmd_dest_num */
			if (j == 0) {
				buf = mms_strapp(buf,
					    "\nwhere \n(\n");
			}
			dest_buf =
				(char *)mm_return_char(&cmd->cmd_dest_list, j);
			if (print_message) {
				mms_trace(MMS_DEVP, "    2) Dest %d -> %s",
				    j, dest_buf);
				mms_trace(MMS_DEVP, "mm_get_path(%s, %s)",
				    dest_buf,
				    source_buf);
			}

			if ((path = mm_get_path(dest_buf,
						source_buf)) == NULL) {

				if (strcmp(dest_buf,
					    source_buf) == 0) {
					/* same object */
					buf = mms_strapp(buf, "(true)\n");
					wrote_one = 1;
				} else {
					/* no path between */
					buf = mms_strapp(buf, "(false)\n");
					wrote_one = 1;
				}

			} else {


			for (k = path->mm_node_num - 1; k >= 0; k--) {
				/* node[%d] has %d edges, k, */
				/* path->mm_node[k]->mm_edge_num */
				for (l = 0;
				    l < path->mm_node[k]->mm_edge_num;
				    l++) {
					/*
					 * if (path->mm_node[k]->
					 *   mm_edge[l]->mm_ref_att != NULL) {
					 *
					 * }
					 */
					/* add this edge constraint */
					wrote_one = 1;
					if (k == path->mm_node_num - 1) {
						buf = mms_strapp(buf,
							    "(\"%s\".",
							    source_buf);
					} else {
						buf = mms_strapp(buf,
							    "(\"%s\".",
							    path->mm_node
							    [k+1]->mm_obj);
					}
					if (path->mm_node[k]->
					    mm_edge[l]->mm_ref_att == NULL) {
						buf = mms_strapp(buf,
						"\"%s\" = ",
						path->mm_node[k]->
						mm_edge[l]->mm_att);
					} else {
						buf = mms_strapp(buf,
						"\"%s\" = ",
						path->mm_node[k]->
						mm_edge[l]->mm_ref_att);
					}
					buf = mms_strapp(buf,
						"\"%s\".\"%s\")\n",
						path->mm_node[k]->mm_obj,
						path->mm_node[k]->
						mm_edge[l]->mm_att);

					if (l+1 < path->mm_node[k]->
					    mm_edge_num) {
						buf = mms_strapp(buf, "and\n");
					}

				}


				if (k - 1 >= 0) {
					buf = mms_strapp(buf, "and\n");
				}

			}

			}
				if (j + 1 < cmd->cmd_dest_num) {
					buf = mms_strapp(buf, "and\n");
				}



				if (j + 1 == cmd->cmd_dest_num) {
					int p;
					/* Add all constraints here */

					for (p = 0;
					    p < cmd->cmd_const_num;
					    p ++) {
						if (p == 0) {
							if (wrote_one)
							buf = mms_strapp(buf,
								"and\n(\n");
						}
						buf = mms_strapp(buf,
							(char *)
							mm_return_char(&cmd->
							cmd_const_list, p));
					if (p+1 < cmd->cmd_const_num) {
								buf =
								mms_strapp(buf,
								    "and\n");
					}
						if (p + 1 ==
						    cmd->cmd_const_num) {
						/* parenn for constraints */
						buf = mms_strapp(buf, ")\n");
						}
					}
					/* paren for where ( */
					if (wrote_one) {
						buf = mms_strapp(buf,
							    ");\n");
					} else {
						buf = mms_strapp(buf,
							    ";\n");
					}
				}

		}


		buf = mms_strapp(buf,
			    "$$ LANGUAGE SQL;\n");

		savepoint = mms_strnew("report_%d_%d", i, db->mm_db_fd);
		if (mm_db_txn_savepoint(db, savepoint) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_sql_report_func: "
			    "db error setting savepoint");
		}
		if (mm_db_exec(HERE, db, "drop function report_%d_%d();", i,
			    db->mm_db_fd) !=
		    MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			if (mm_db_txn_savepoint_rollback(db,
			    savepoint) != MM_DB_OK) {
				mms_trace(MMS_ERR,
				    "mm_sql_report_func: "
				    "db error rollingback savepoint");
			}
		}
		if (mm_db_txn_release_savepoint(db,
		    savepoint) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_sql_report_func: "
			    "db error releaseing savepoint");
		}
		free(savepoint);

		if (mm_db_exec(HERE, db, buf) != MM_DB_OK) {
			mms_trace(MMS_ERR, "Error adding path match function");
			mm_sql_db_err_rsp_new(cmd, db);
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			free(buf);
			return (1);
		}
		free(buf);
		buf = NULL;

		for (y = 0; y < joined_count; y ++) {
			free(joined[y]);
			joined[y] = NULL;
		}


	}
	return (0);

}

int
mm_sql_report_func_attr(mm_command_t *cmd) {
	char *buf = NULL;
	char *order_buf = NULL;
	mm_path_t *path = NULL;
	char *joined[100];
	int joined_count = 0;
	int skip = 0;
	mm_db_t	*db = &cmd->cmd_mm_data->mm_db;
	int wrote_one = 0;
	char *temp_buf = NULL;
	char *savepoint = NULL;

	char *source_buf;
	char *dest_buf;
	int y;
	int j;
	int k;
	int x;
	int l;
	int p;
	int i;

	mms_trace(MMS_DEBUG, "mm_sql_make_sql_func_attr");

	for (y = 0; y < 100; y ++) {
		joined[y] = NULL;
	}

	/* create report functins for each report onject */
	for (i = 0; i < cmd->cmd_source_num; i ++) { /* source */
		joined_count = 0;

		/* Make report_n() for this source */

		source_buf =
			(char *)mm_return_char(&cmd->cmd_source_list, i);
		buf = mms_strapp(buf,
			    "CREATE OR REPLACE FUNCTION "\
			    "report_%d_%d() RETURNS SETOF \"%s\" AS $$\n",
			    i, db->mm_db_fd, source_buf);
		buf = mms_strapp(buf,
			    "select distinct \"%s\".* from \"%s\" ",
			    source_buf,
			    source_buf);
		joined_count = 0;
		joined[0] = NULL;
		joined[0] = mms_strapp(joined[0], source_buf);


		joined_count ++;
		for (j = 0; j < cmd->cmd_dest_num; j ++) {
			dest_buf =
				(char *)mm_return_char(&cmd->cmd_dest_list, j);

			if ((path = mm_get_path(dest_buf,
						source_buf)) == NULL) {
				mms_trace(MMS_DEVP, "Path is NULL, %s to %s",
				    dest_buf,
				    source_buf);

			} else {
			for (k = 0; k < path->mm_node_num; k++) {
				skip = 0;
				for (x = 0; x < joined_count; x++) {
					if (strcmp(joined[x],
					path->mm_node[k]->mm_obj) == 0) {
						/* same so skip */
						skip = 1;
					}
				}
				if (!skip) {
					buf = mms_strapp(buf,
						    "\ncross join \"%s\" ",
						    path->mm_node[k]->mm_obj);
					joined[joined_count] = NULL;
					joined[joined_count] =
						mms_strapp(joined[joined_count],
						path->mm_node[k]->mm_obj);
					joined_count ++;
				}
			}
			}

		}

		order_buf = mm_sql_order_buf(cmd);
		buf = mms_strapp(buf, order_buf);
		if (order_buf != NULL)
			free(order_buf);

		for (j = 0; j < cmd->cmd_dest_num; j ++) {
			dest_buf =
				(char *)mm_return_char(&cmd->cmd_dest_list, j);
			/* j cmd_dest_num */
			if (j == 0) {
				buf = mms_strapp(buf,
					    "\nwhere \n(\n");

			}

			if ((path = mm_get_path(dest_buf,
						source_buf)) == NULL) {

				if (strcmp(dest_buf,
					    source_buf) == 0) {
					/* same object */
					buf = mms_strapp(buf, "(true)\n");
					wrote_one = 1;
				} else {
					/* no path between */
					buf = mms_strapp(buf, "(false)\n");
					wrote_one = 1;
				}

			} else {


			for (k = path->mm_node_num - 1; k >= 0; k--) {

				for (l = 0;
				    l < path->mm_node[k]->mm_edge_num;
				    l++) {
					/*
					 * if (path->mm_node[k]->
					 *   mm_edge[l]->mm_ref_att != NULL) {
					 *
					 * }
					 */

					/* add this edge constraint */
					wrote_one = 1;
					if (k == path->mm_node_num - 1) {
						buf = mms_strapp(buf,
							    "(\"%s\".",
							    source_buf);
					} else {
						buf = mms_strapp(buf,
							    "(\"%s\".",
							    path->mm_node
							    [k+1]->mm_obj);
					}
					if (path->mm_node[k]->
					    mm_edge[l]->mm_ref_att == NULL) {
						buf = mms_strapp(buf,
						"\"%s\" = ",
						path->mm_node[k]->
						mm_edge[l]->mm_att);
					} else {
						buf = mms_strapp(buf,
						"\"%s\" = ",
						path->mm_node[k]->
						mm_edge[l]->mm_ref_att);
					}
					buf = mms_strapp(buf,
						"\"%s\".\"%s\")\n",
						path->mm_node[k]->mm_obj,
						path->mm_node[k]->
						mm_edge[l]->mm_att);

					if (l+1 < path->mm_node[k]->
					    mm_edge_num) {
						buf = mms_strapp(buf, "and\n");
					}

				}


				if (k - 1 >= 0) {
					buf = mms_strapp(buf, "and\n");
				}

			}

			}
				if (j + 1 < cmd->cmd_dest_num) {
					buf = mms_strapp(buf, "and\n");
				}



				if (j + 1 == cmd->cmd_dest_num) {
					/* Add all constraints here */

					for (p = 0;
					    p < cmd->cmd_const_num;
					    p ++) {
						if (p == 0) {
							if (wrote_one)
							buf = mms_strapp(buf,
								"and\n(\n");
						}
						buf = mms_strapp(buf,
							(char *)
							mm_return_char(&cmd->
							cmd_const_list, p));
					if (p+1 < cmd->cmd_const_num) {
								buf =
								mms_strapp(buf,
								    "and\n");
					}
						if (p + 1 ==
						    cmd->cmd_const_num) {
						/* parenn for constraints */
						buf = mms_strapp(buf, ")\n");
						}
					}

					/* paren for where ( */
					if (wrote_one) {
						buf = mms_strapp(buf,
							    ")\n");
					} else {
						buf = mms_strapp(buf,
							    "\n");
					}
				}

		}

		temp_buf = mms_strapp(temp_buf, mm_sql_number_buf(cmd));
		if (temp_buf != NULL) {
			buf = mms_strapp(buf, temp_buf);
			free(temp_buf);
		}

		buf = mms_strapp(buf,
			    " ; $$ LANGUAGE SQL;\n");


		savepoint = mms_strnew("report_%d_%d", i, db->mm_db_fd);
		if (mm_db_txn_savepoint(db,
		    savepoint) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_sql_report_func_attr: "
			    "db error setting savepoint");
		}
		if (mm_db_exec(HERE, db, "drop function report_%d_%d();", i,
			    db->mm_db_fd) !=
		    MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			if (mm_db_txn_savepoint_rollback(db,
			    savepoint) != MM_DB_OK) {
				mms_trace(MMS_ERR,
				    "mm_sql_report_func_attr: "
				    "db error setting savepoint");
			}
		}
		if (mm_db_txn_release_savepoint(db,
		    savepoint) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_sql_report_func_attr: "
			    "db error releaseing savepoint");
		}
		free(savepoint);

		if (mm_db_exec(HERE, db, buf) != MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			mms_trace(MMS_ERR, "Error adding path match function");
			return (1);
		}
		free(buf);
		buf = NULL;

		for (y = 0; y < joined_count; y ++) {
			free(joined[y]);
			joined[y] = NULL;
		}


	}
	return (0);
}


int
mm_sql_from_where(mm_command_t *cmd, mm_db_t *db)
{
	char *buf = NULL;
	char *final_buf = NULL;
	mm_path_t *path = NULL;
	char *joined[100];
	int joined_count = 0;
	int skip = 0;
	mm_pkey_t *p_key = NULL;

	char *source_buf_i;
	char *source_buf_j;
	int y;
	int i;
	int j;
	int k;
	int x;
	int l;

	mms_trace(MMS_DEVP, "mm_sql_from_where");

	for (y = 0; y < 100; y ++) {
		joined[y] = NULL;
	}
	/* Generate all report_n() for this command */

	if (mm_sql_report_func(cmd, db)) {
		mms_trace(MMS_ERR,
		    "error creating report funcs");
		return (1);
	}

	joined_count = 0;

	for (i = 0; i < cmd->cmd_source_num; i ++) {

		/*
		 * ex, i = 0
		 * report_0() t0, "DM"
		 * t0 is arbitrary table alias
		 */
		source_buf_i = (char *)mm_return_char(&cmd->cmd_source_list, i);
		buf = mms_strapp(buf,
		    "\nreport_%d_%d() t%d, \"%s\" ",
		    i, db->mm_db_fd, i, source_buf_i);
		joined[i] = NULL;
		joined[i] = mms_strapp(joined[i],
		    source_buf_i);
		joined_count ++;

		if (i+1 < cmd->cmd_source_num) {
			buf = mms_strapp(buf,
			    "cross join ");
		}
	}

	/* Add objects to FROM clause */
	/* add implied constraints between source objects */


	for (i = 0; i < cmd->cmd_source_num; i ++) {
		source_buf_i = (char *)mm_return_char(&cmd->cmd_source_list, i);
		for (j = i+1; j < cmd->cmd_source_num; j ++) {
			source_buf_j = (char *)
			    mm_return_char(&cmd->cmd_source_list, j);
			if ((path = mm_get_path(source_buf_j,
			    source_buf_i)) != NULL) {

				for (k = 0; k < path->mm_node_num; k++) {
					skip = 0;
					for (x = 0; x < joined_count; x++) {

						if (strcmp(joined[x],
						    path->mm_node[k]->
						    mm_obj) == 0) {
							/* same so skip */
							skip = 1;
						}
					}
				if (!skip) {
					buf = mms_strapp(buf,
					    "\ncross join \n\"%s\" ",
					    path->mm_node[k]->mm_obj);

					joined[joined_count] = NULL;
					joined[joined_count] =
					    mms_strapp(joined[joined_count],
					    path->mm_node[k]->mm_obj);
					joined_count ++;
				}

				}
			}

		}
	}

	buf = mms_strapp(buf, "\nwhere \n(\n");

	for (i = 0; i < cmd->cmd_source_num; i ++) {
		source_buf_i = (char *)mm_return_char(&cmd->cmd_source_list, i);
		p_key = mm_get_pkey(source_buf_i);
		for (j = 0; j < p_key->mm_att_num; j ++) {
			buf = mms_strapp(buf,
			    "(t%d.\"%s\" = \"%s\".\"%s\")\n",
			    i, p_key->mm_att[j], source_buf_i,
			    p_key->mm_att[j]);
			if (j+1 < p_key->mm_att_num) {
				buf = mms_strapp(buf, "and\n");
			}
		}
		if (i+1 < cmd->cmd_source_num) {
			buf = mms_strapp(buf, "and\n");
		}
	}


	/* Now add the implied path constraints */

	for (i = 0; i < cmd->cmd_source_num; i ++) {
		source_buf_i = (char *)mm_return_char(&cmd->cmd_source_list, i);
		for (j = i+1; j < cmd->cmd_source_num; j ++) {
			source_buf_j = (char *)
			    mm_return_char(&cmd->cmd_source_list, j);
			if ((path = mm_get_path(source_buf_j,
			    source_buf_i)) != NULL) {
			for (k = path->mm_node_num - 1; k >= 0; k--) {
				for (l = 0;
				    l < path->mm_node[k]->mm_edge_num;
				    l++) {
					/*
					 * if (path->mm_node[k]->
					 *   mm_edge[l]->mm_ref_att != NULL) {
					 *
					 * }
					 */

					/* add this edge constraint */
					/* will always need an 'and' */
					buf = mms_strapp(buf, "and\n");
					if (k == path->mm_node_num - 1) {
						buf = mms_strapp(buf,
						    "(\"%s\".",
						    source_buf_i);
					} else {
						buf = mms_strapp(buf,
						    "(\"%s\".",
						    path->mm_node
						    [k+1]->mm_obj);
					}
					if (path->mm_node[k]->
					    mm_edge[l]->mm_ref_att == NULL) {
						buf = mms_strapp(buf,
						    "\"%s\" = ",
						    path->mm_node[k]->
						    mm_edge[l]->mm_att);
					} else {
						buf = mms_strapp(buf,
						    "\"%s\" = ",
						    path->mm_node[k]->
						    mm_edge[l]->mm_ref_att);
					}
					buf = mms_strapp(buf,
					    "\"%s\".\"%s\")\n",
					    path->mm_node[k]->mm_obj,
					    path->mm_node[k]->
					    mm_edge[l]->mm_att);
				}
			}
			}
		}
	}

	buf = mms_strapp(buf, ")\n");

	final_buf = mms_strapp(final_buf, cmd->cmd_buf);
	final_buf = mms_strapp(final_buf, buf);

	SQL_CHK_LEN(&cmd->cmd_buf, NULL,
	    &cmd->cmd_bufsize, strlen(final_buf) + 1);
	strcpy(cmd->cmd_buf, final_buf);


	for (y = 0; y < joined_count; y ++) {
		free(joined[y]);
		joined[y] = NULL;
	}
	free(buf);
	free(final_buf);
	return (0);

no_mem:
	MM_ABORT_NO_MEM();
	return (1);


}



int
mm_attribute_match(mm_command_t *cmd) {

	char *buf = NULL;
	mm_db_t	*db = &cmd->cmd_mm_data->mm_db;
	mm_pkey_t *p_key = NULL;

	int j;
	int i;

	char *source_buf_0;

	mms_trace(MMS_DEBUG, "mm_attribute_match");
	buf = mms_strapp(buf, cmd->cmd_buf);
	mms_trace(MMS_DEVP, "buf is %s",
	    buf);

	buf = mms_strapp(buf, "\nwhere\n");
	/* get pkey for this source */
	source_buf_0 = (char *)mm_return_char(&cmd->cmd_source_list, 0);
	p_key = mm_get_pkey(source_buf_0);
	buf = mms_strapp(buf, "(\n");

	for (j = 0; j < p_key->mm_att_num; j ++) {
		buf = mms_strapp(buf,
			    "(\"%s\".\"%s\" in \n",
			    source_buf_0,
			    p_key->mm_att[j]);
		buf = mms_strapp(buf,
			    "(select \"%s\".\"%s\" from "\
			    "report_0_%d() t%d, \"%s\" where\n",
			    source_buf_0,
			    p_key->mm_att[j],
			    db->mm_db_fd,
			    j,
			    source_buf_0);

		buf = mms_strapp(buf, "(\n");
		for (i = 0; i < p_key->mm_att_num; i ++) {

			buf = mms_strapp(buf,
				    "(t%d.\"%s\" = \"%s\".\"%s\") ",
				    j,
				    p_key->mm_att[i],
				    source_buf_0,
				    p_key->mm_att[i]);

			if (i+1 < p_key->mm_att_num) {
				buf = mms_strapp(buf, "and\n");
			}
		}
		buf = mms_strapp(buf, ")\n");
		buf = mms_strapp(buf, ")\n");
		buf = mms_strapp(buf, ")\n");
		if (j+1 < p_key->mm_att_num) {
			buf = mms_strapp(buf, "and\n");
		}
	}

	buf = mms_strapp(buf, ")\n");


	SQL_CHK_LEN(&cmd->cmd_buf, NULL,
		    &cmd->cmd_bufsize, strlen(buf) + 1);
	strcpy(cmd->cmd_buf, buf);

	free(buf);

	return (INTRP_OK);



no_mem:
	MM_ABORT_NO_MEM();
	return (MM_CMD_ERROR);

}

static int
mm_sql_trans_match_exp(mms_par_node_t *node, mm_command_t *cmd,
    int *offset, int host_ident)
{
	mms_par_node_t	*arg;
	mms_par_node_t	*next_arg;
	int		 off = *offset;
	char		*sql_ops;
	const char	*att_test;
	char		*att_cond;
	char		*obj;
	char		*att;

	int		need_int_cast = 0;
	int		need_host_ident = 0;

	int		print_message = 0;

	mms_trace(MMS_DEVP, "sql trans match exp");
	if (cmd->cmd_buf != NULL) {
		if (print_message)
		mms_trace(MMS_DEVP,
		    "    cmdbuf == %s",
		    cmd->cmd_buf);
	} else {
		if (print_message)
			mms_trace(MMS_DEVP,
			    "    cmd_buf is NULL");
	}

	if (node->pn_type & MMS_PN_STRING) {
		if (print_message)
			mms_trace(MMS_DEVP,
			    " (node->pn_type & MMS_PN_STRING)");

		/* Could be numeric or null string or string. */
		if (node->pn_type == MMS_PN_NUMERIC) {
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    1 + strlen(node->pn_string));
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off,
			    "%s", node->pn_string);
		} else if (node->pn_type & MMS_PN_NULLSTR) {
			/* Still support NULLSTR ?? */
			if (strcmp(&cmd->cmd_buf[off - 3], " = ") == 0) {
				off -= 2; /* backup over operator */
				SQL_CHK_LEN(&cmd->cmd_buf,
				    off, &cmd->cmd_bufsize, 7);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, "ISNULL");
			} else if (strcmp(&cmd->cmd_buf[off - 4],
			    " <= ") == 0 ||
			    strcmp(&cmd->cmd_buf[off - 4],
			    " >= ") == 0) {
				off -= 3; /* backup over operator */
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 7);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, "ISNULL");
			} else if (strcmp(&cmd->cmd_buf[off - 4],
			    " <> ") == 0) {
				off -= 3; /* backup over operator */
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 8);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, "NOTNULL");
			} else {
				SQL_CHK_LEN(&cmd->cmd_buf,
				    off, &cmd->cmd_bufsize,
				    1 + strlen(node->pn_string));
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off,
				    "%s", node->pn_string);
			}
		} else {
			if (host_ident) {
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 15);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off,
				    "pg_host_ident(");
			}
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    3 + strlen(node->pn_string));
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off,
			    "'%s'", node->pn_string);
			if (host_ident) {
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 2);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, ")");
			}
		}

	}

	if (node->pn_type == MMS_PN_OBJ) {
		if (print_message)
			mms_trace(MMS_DEVP,
			    " node->pn_type == MMS_PN_OBJ");

		/* An object-attribute */
		if (cmd->cmd_notify_to == 1) {
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    2 + strlen("NEW"));
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off, "%s.", "NEW");
		} else {
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    4 + strlen(node->pn_string));
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off,
			    "\"%s\".", node->pn_string);
		}
		arg = mms_list_head(&node->pn_arglist);
		SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
		    3 + strlen(arg->pn_string));
		off += snprintf(cmd->cmd_buf + off,
		    cmd->cmd_bufsize - off,
		    "\"%s\"", arg->pn_string);
	} else if (node->pn_flags & MMS_PN_MULTIOPS) {

		if (print_message)
			mms_trace(MMS_DEVP,
			    "(node->pn_flags & MMS_PN_MULTIOPS)");
		if (node->pn_string != NULL) {
			if ((strcmp(node->pn_string,
			    "numeq") == 0) ||
			    (strcmp(node->pn_string,
			    "numne") == 0) ||
			    (strcmp(node->pn_string,
			    "numlt") == 0) ||
			    (strcmp(node->pn_string,
			    "numle") == 0) ||
			    (strcmp(node->pn_string,
			    "numgt") == 0) ||
			    (strcmp(node->pn_string,
			    "numge") == 0)) {
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 6);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, "int4(");
				need_int_cast = 1;
			}
			if (print_message)
				mms_trace(MMS_DEVP,
				    "    %s",
				    node->pn_string);
			if ((strcmp(node->pn_string,
			    "hosteq") == 0) ||
			    (strcmp(node->pn_string,
			    "hostne") == 0)) {
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 15);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off,
				    "pg_host_ident(");
				need_int_cast = 1;
				need_host_ident = 1;
			}
		}

		/*
		 * A multiops has two or more args hanging off the arglist.
		 * Insert the ops between the args by adding an arg, then add
		 * the ops and repeat until the last arg.
		 */
		if (node->pn_flags & MMS_PN_REGEX) {
			/* sql regular expression is at end of list */
			arg = mms_list_head(&node->pn_arglist);
			mms_list_remove(&node->pn_arglist, arg);
			mms_list_insert_tail(&node->pn_arglist, arg);
		}
		mms_list_foreach(&node->pn_arglist, arg) {
			if (arg->pn_type == MMS_PN_OPS) {
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 2);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, "(");
			}

			if (mm_sql_trans_match_exp(arg, cmd,
			    &off, need_host_ident)) {
				mms_trace(MMS_ERR,
				    "mm_sql_trans_match_exp: "
				    "error translating match expression");
				return (1);
			}
			if (arg->pn_type == MMS_PN_OPS) {
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 2);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, ")");
			}
			if (need_int_cast) {
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize, 2);
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off, ")");
				need_int_cast = 0;
			}
			if (arg != mms_list_tail(&node->pn_arglist)) {
				sql_ops = mm_sql_get_ops(node->pn_string);
				SQL_CHK_LEN(&cmd->cmd_buf, off,
				    &cmd->cmd_bufsize,
				    3 + strlen(sql_ops));
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off,
				    " %s ", sql_ops);
			}
		}

		if (node->pn_flags & MMS_PN_REGEX) {
			/* restore list to original order */
			arg = mms_list_tail(&node->pn_arglist);
			mms_list_remove(&node->pn_arglist, arg);
			mms_list_insert_head(&node->pn_arglist, arg);
		}
	} else if (node->pn_flags & MMS_PN_UNARYOPS) {
		if (print_message)
			mms_trace(MMS_DEVP,
			    "(node->pn_flags & MMS_PN_UNARYOPS)");

		/* Unary ops has only one arg */
		if (strcmp(node->pn_string, "isset") == 0 ||
		    strcmp(node->pn_string, "notset") == 0) {
			arg = mms_list_head(&node->pn_arglist);

			if (mm_sql_trans_match_exp(arg, cmd,
			    &off, need_host_ident)) {
				mms_trace(MMS_ERR,
				    "mm_sql_trans_match_exp: "
				    "error translating match expression");
				return (1);
			}
			if (strcmp(node->pn_string, "isset") == 0) {
				/* isset */
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off,
				    " IS NOT NULL");
			} else {
				/* notnot */
				off += snprintf(cmd->cmd_buf + off,
				    cmd->cmd_bufsize - off,
				    " ISNULL");
			}


		} else if (strcmp(node->pn_string, "isattr") == 0 ||
		    strcmp(node->pn_string, "noattr") == 0) {

			att_test = "mm_obj_has_att('%s','%s') = '%s'";

			if (strcmp(node->pn_string, "isattr") == 0) {
				att_cond = "true";
			} else {
				att_cond = "false";
			}

			arg = mms_list_head(&node->pn_arglist);
			obj = arg->pn_string;

			next_arg = mms_list_head(&arg->pn_arglist);
			att = next_arg->pn_string;

			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    strlen(att_test) + 1);
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off,
			    att_test, obj, att, att_cond);

		} else {
			sql_ops = mm_sql_get_ops(node->pn_string);
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    strlen(sql_ops) + 3);
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off,
			    " %s ", sql_ops);
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize, 2);
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off, "(");
			arg = mms_list_head(&node->pn_arglist);

			if (mm_sql_trans_match_exp(arg, cmd,
			    &off, need_host_ident)) {
				mms_trace(MMS_ERR,
				    "mm_sql_trans_match_exp: "
				    "error translating match expression");
				return (1);
			}
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize, 2);
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off, ")");
		}
	}
	*offset = off;
	return (0);

no_mem:
	MM_ABORT_NO_MEM();
	return (1);

not_found:
	mm_response_error(cmd,
	    ECLASS_LANGUAGE,
	    ENOTFOUND,
	    MM_5062_MSG,
	    NULL);
	return (1);

}


void
mm_sql_number(mm_command_t *cmd)
{


	char		*buf = NULL;
	char		*final_buf = NULL;
	char		*number_buf = NULL;

	mms_trace(MMS_DEVP, "mm_sql_number");
	/* This function will append the number buf to cmd_buf */

	number_buf = mm_sql_number_buf(cmd);

	buf = mms_strapp(buf, number_buf);

	if (number_buf != NULL)
		free(number_buf);

	if (buf == NULL) {
		return;
	}

	final_buf = mms_strapp(final_buf, cmd->cmd_buf);
	final_buf = mms_strapp(final_buf, buf);

	SQL_CHK_LEN(&cmd->cmd_buf, NULL,
	    &cmd->cmd_bufsize, strlen(final_buf) + 1);
	strcpy(cmd->cmd_buf, final_buf);

	if (buf)
		free(buf);
	if (final_buf)
		free(final_buf);

	return;

no_mem:
	MM_ABORT_NO_MEM();
}



char *
mm_sql_number_buf(mm_command_t *cmd)
{
	/* This function returns a buf of the number clause */
	mms_par_node_t	*number;
	mms_par_node_t	*range;
	mms_par_node_t	*node;
	mms_par_node_t	*arg1;
	mms_par_node_t	*arg2;
	int		 range1 = 0;
	int		 range2 = 0;

	int		 val;

	char		*buf = NULL;
	mms_par_node_t	*work;



	mms_trace(MMS_DEVP, "mm_sql_number_buf");
	number = mms_pn_lookup(cmd->cmd_root, "number",
	    MMS_PN_CLAUSE, NULL);
	if (number == NULL) {
		mms_trace(MMS_DEVP, "didn't find a number clause");
		return (NULL);
	}
	/*
	 * Subtract one from the number because the postgres data index
	 * starts at zero and the mmp command number starts at one.
	 */
	node = mms_list_head(&number->pn_arglist);



	if ((range = mms_pn_lookup(number, NULL,
	    MMS_PN_RANGE, NULL)) != NULL) {
		mms_trace(MMS_DEVP, "inside range..");

		work = NULL;
		/* Have a range */
		/* get the 1st and last arg */
		arg1 = mms_pn_lookup(range, NULL,
		    MMS_PN_STRING, &work);
		arg2 = mms_pn_lookup(range, NULL,
		    MMS_PN_STRING, &work);

		if ((arg1->pn_string == NULL) ||
		    (arg2->pn_string == NULL)) {
			mms_trace(MMS_ERR,
			    "an arg in range cannot be NULL");
			return (buf);
		}

		if (strcmp(arg1->pn_string, "FIRST") == 0) {
			if (arg2->pn_type & MMS_PN_NUMERIC) {
				range1 = 1;
				sscanf(arg2->pn_string, "%d", &range2);
			} else {
				return (buf);
			}
		} else if (strcmp(arg2->pn_string, "LAST") == 0) {
			if (arg1->pn_type & MMS_PN_NUMERIC) {
				sscanf(arg1->pn_string, "%d", &val);
				val -= 1;
				buf = mms_strapp(buf, " OFFSET %d ", val);

				return (buf);
			} else {
				return (buf);
			}
		} else if (arg1->pn_type & MMS_PN_NUMERIC &&
		    arg2->pn_type & MMS_PN_NUMERIC) {
			sscanf(arg1->pn_string, "%d", &range1);
			sscanf(arg2->pn_string, "%d", &range2);
		} else {
			return (buf);
		}
		range1 -= 1;
		range2 -= 1;
		buf = mms_strapp(buf, " LIMIT %d OFFSET %d ",
		    range2 - range1 + 1, range1);

	} else if (strcmp(node->pn_string, "FIRST") == 0) {
		buf = mms_strapp(buf, " LIMIT 1 OFFSET 0 ");

	} else if (node->pn_type & MMS_PN_NUMERIC) {
		sscanf(node->pn_string, "%d", &val);
		val -= 1;
		buf = mms_strapp(buf, " LIMIT 1 OFFSET %d ", val);

	}

	if (buf == NULL) {
		mms_trace(MMS_ERR,
		    "found a number clause, "
		    "but didn't write any sql");
		mms_trace(MMS_DEVP, "Number buf is NULL");
		return (buf);
	}




	return (buf);


no_mem:
	MM_ABORT_NO_MEM();
	return (NULL);

not_found:
	return (NULL);

}



int
mm_get_range(mm_command_t *cmd, mm_range_t *range)
{
	mms_par_node_t	*number;
	mms_par_node_t	*node;
	mms_par_node_t	*arg1;
	mms_par_node_t	*arg2;

	/*
	 * Get number clause range, number[a..b] or number[a]
	 */

	memset(range, 0, sizeof (mm_range_t));

	if ((number = mms_pn_lookup(cmd->cmd_root, "number",
	    MMS_PN_CLAUSE, NULL)) == NULL) {
		/* no number */
		range->mm_range_type = MM_RANGE_NONE;
		return (0);
	}

	node = mms_list_head(&number->pn_arglist);
	if (node->pn_type == MMS_PN_RANGE) {

		/* number range */
		arg1 = mms_list_head(&node->pn_arglist);
		arg2 = mms_list_tail(&node->pn_arglist);

		if (strcmp(arg1->pn_string, "FIRST") == 0 &&
		    strcmp(arg2->pn_string, "LAST") == 0) {
			mms_trace(MMS_DEVP, "number-clause range first-last");
			range->mm_range_type = MM_RANGE_FIRST_LAST;
			range->mm_range_first = 1;
			return (0);
		} else if (strcmp(arg1->pn_string, "FIRST") == 0) {
			range->mm_range_type = MM_RANGE_FIRST;
			range->mm_range_first = 1;
			if (arg2->pn_type & MMS_PN_NUMERIC) {
				range->mm_range_last =
				    atoi(arg2->pn_string);
				mms_trace(MMS_DEVP,
				    "number-clause range first-%d",
				    range->mm_range_last);
				return (0);
			}
		} else if (strcmp(arg2->pn_string, "LAST") == 0) {
			range->mm_range_type = MM_RANGE_LAST;
			if (arg1->pn_type & MMS_PN_NUMERIC) {
				range->mm_range_first =
				    atoi(arg1->pn_string);
				mms_trace(MMS_DEVP, "number-clause "
				    "range %d-last",
				    range->mm_range_first);
				return (0);
			}
		} else if (arg1->pn_type & MMS_PN_NUMERIC &&
		    arg2->pn_type & MMS_PN_NUMERIC) {
			range->mm_range_type = MM_RANGE_NUMS;
			range->mm_range_first = atoi(arg1->pn_string);
			range->mm_range_last = atoi(arg2->pn_string);
			mms_trace(MMS_DEVP, "number-clause range %d-%d",
			    range->mm_range_first, range->mm_range_last);
			return (0);
		}

		mms_trace(MMS_DEVP, "number-clause range lookup failed");
		return (1);

	}

	/* single number */
	range->mm_range_type = MM_RANGE_A_NUM;
	range->mm_range_first = atoi(node->pn_string);
	mms_trace(MMS_DEVP, "number-clause number %d",
	    range->mm_range_first);
	return (0);
}

void
mm_sql_order(mm_command_t *cmd)
{
	char		*buf = NULL;
	char		*final_buf = NULL;
	char		*order_buf = NULL;

	mms_trace(MMS_DEVP, "mm_sql_order");

	order_buf = mm_sql_order_buf(cmd);

	buf = mms_strapp(buf, order_buf);
	if (order_buf != NULL)
		free(order_buf);
	if (buf == NULL) {
		return;
	}

	final_buf = mms_strapp(final_buf, cmd->cmd_buf);
	final_buf = mms_strapp(final_buf, buf);

	SQL_CHK_LEN(&cmd->cmd_buf, NULL,
	    &cmd->cmd_bufsize, strlen(final_buf) + 1);


	strcpy(cmd->cmd_buf, final_buf);


	if (buf)
		free(buf);
	if (final_buf)
		free(final_buf);

	return;

no_mem:
	MM_ABORT_NO_MEM();
}

char *
mm_sql_order_buf(mm_command_t *cmd)
{
	mms_par_node_t	*order;
	mms_par_node_t	*ops;
	mms_par_node_t	*object;
	mms_par_node_t	*attr;

	mms_par_node_t	*work = NULL;
	int		 ordercnt = 0;
	char		*sql_ops;

	char		*buf = NULL;

	int		need_int_cast = 0;

	mms_trace(MMS_DEVP, "mm_sql_order_buf");

	for (order = mms_pn_lookup(cmd->cmd_root, "order",
	    MMS_PN_CLAUSE, &work);
	    order != NULL;
	    ordercnt++,
	    order = mms_pn_lookup(cmd->cmd_root, "order",
	    MMS_PN_CLAUSE, &work)) {
		if (ordercnt == 0) {
			buf = mms_strapp(buf, "ORDER BY ");
		} else {
			buf = mms_strapp(buf, ", ");
		}
		ops = mms_list_head(&order->pn_arglist);
		sql_ops = mm_sql_get_ops(ops->pn_string);
		mms_trace(MMS_DEVP,
		    "op == %s",
		    ops->pn_string);

		if ((strcmp(ops->pn_string,
		    "numhilo") == 0) ||
		    (strcmp(ops->pn_string,
		    "numlohi") == 0)) {
			need_int_cast = 1;
			buf = mms_strapp(buf,
			    "int4(");
		}

		object = mms_list_head(&ops->pn_arglist);
		buf = mms_strapp(buf, "\"%s\".",
		    object->pn_string);

		attr = mms_list_head(&object->pn_arglist);
		buf = mms_strapp(buf, "\"%s\" ",
		    attr->pn_string);
		if (need_int_cast) {
			buf = mms_strapp(buf, ") ");
		}

		buf = mms_strapp(buf, "%s", sql_ops);

	}
	if (buf == NULL) {
		mms_trace(MMS_DEVP, "Order buf is NULL");
		buf = mms_strapp(buf, " ");
		return (buf);
	}

	return (buf);

no_mem:
	MM_ABORT_NO_MEM();
	return (NULL);

}


void
mm_sql_db_err_rsp_new(mm_command_t *cmd, mm_db_t *db)
{
	int		 dbstatus;
	char		*dbmessage;

	dbstatus = PQresultStatus(db->mm_db_results);
	dbmessage = PQresultErrorMessage(db->
	    mm_db_results);

	free(cmd->cmd_buf);
	if ((cmd->cmd_buf = mm_db_sql_err_rsp(dbstatus, dbmessage,
	    cmd->wka_ptr->wka_conn.cci_language, cmd->cmd_task)) == NULL) {
		MM_ABORT_NO_MEM();
		return;
	}
	mm_clear_db(&db->mm_db_results);

}

int
mm_add_match_list(char *str, mms_list_t *list) {
	/* Checks is str is already in list */
	/* If str exists, do nothing */
	/* Else add to list and inc num */
	if (mm_in_char_list(list, str)) {
		return (1);
	}
	if (mm_add_char(str, list)) {
		mms_trace(MMS_ERR,
		    "mm_add_match_list: "
		    "error adding char");
		return (1);
	}
	return (0);
}

int
/* LINTED: mm_wka may be used in the future */
mm_get_const(mm_wka_t *mm_wka, mm_command_t *cmd)
{
	mms_par_node_t	*match;
	mms_par_node_t	*node;
	int		  off = 0;
	mms_par_node_t	*vol_name;

	mms_trace(MMS_DEVP, "mm_get_const");

	if (cmd->cmd_buf != NULL) {
		free(cmd->cmd_buf);
		cmd->cmd_buf = NULL;
		cmd->cmd_bufsize = 0;
	}

	/* Look up the match clause */
	match = mms_pn_lookup(cmd->cmd_root, "match",
	    MMS_PN_CLAUSE, NULL);
	if (match == NULL) {
		/* No match clause, chek for volname */
		vol_name = mms_pn_lookup(cmd->cmd_root, "volname",
		    MMS_PN_CLAUSE, NULL);
		if (vol_name != NULL) {
			/* Found a volname */
			mm_sql_trans_volname(cmd, &off);
		} else {
			/* No match and no volname */
			return (1);
		}
		if (cmd->cmd_buf != NULL) {
			mms_trace(MMS_DEVP, "cmd_buf is \n%s\n", cmd->cmd_buf);
		}
		mms_trace(MMS_DEVP, "cmd_const_num = %d", cmd->cmd_const_num);

		if (mm_add_char(cmd->cmd_buf,
		    &cmd->cmd_const_list)) {
			mms_trace(MMS_ERR, "Error adding char to const list"
			    "- out of memory");
			return (1);
		}

		cmd->cmd_const_num ++;
		return (0);
	}
	/* We have a match clause */
	node = mms_list_head(&match->pn_arglist);
	if (node == NULL) {
		mms_trace(MMS_ERR, "Error translating match expression");
		return (1);
	}
	if (mm_sql_trans_match_exp(node, cmd, &off, 0)) {
		mms_trace(MMS_ERR, "Error translating match expression");
		return (1);
	}

	/* List way */
	if (mm_add_char(cmd->cmd_buf,
	    &cmd->cmd_const_list)) {
		mms_trace(MMS_ERR, "Error adding char to source list");
		return (1);
	}

	/* Array Way */

	cmd->cmd_const_num ++;

	/* List TEST */
	mms_trace(MMS_DEVP, "Printing char_list const...");
	mm_print_char_list(&cmd->cmd_const_list);
	return (0);



no_mem:
	MM_ABORT_NO_MEM();
	return (MM_CMD_ERROR);
}





int
/* LINTED: mm_wka may be used in the future */
mm_get_source(mm_wka_t *mm_wka, mm_command_t *cmd)
{
	mms_par_node_t	 *report;
	mms_par_node_t	 *object;
	mms_par_node_t	*work;
	int		source_count = 0;
	int skip = 0;

	mms_trace(MMS_DEBUG, "mm_get_source");

	report = mms_pn_lookup(cmd->cmd_root, "report",
	    MMS_PN_CLAUSE, NULL);
	if (report == NULL) {
		cmd->cmd_source_num = 0;
		return (1);
	}
	work = NULL;
	for (object = mms_pn_lookup(report, NULL, MMS_PN_OBJ, &work);
	    object != NULL;
	    object = mms_pn_lookup(report, NULL, MMS_PN_OBJ, &work)) {
		skip = 0;
		if (mm_in_char_list(&cmd->cmd_source_list,
		    object->pn_string)) {
			/* already have this as a source */
			skip = 1;
		}

		if (!skip) {
			/* List way */
			if (mm_add_char(object->pn_string,
			    &cmd->cmd_source_list)) {
				mms_trace(MMS_ERR, "Error adding "
				    "char to source list");
				return (1);
			}

			/* Array Way */

			source_count ++;
		}

	}
	cmd->cmd_source_num = source_count;
	/* List TEST */
	mms_trace(MMS_DEVP, "Printing char_list source...");
	mm_print_char_list(&cmd->cmd_source_list);
	return (0);

}

void
mm_get_dest_helper(mms_par_node_t *node, mm_command_t *cmd)
{
	mms_par_node_t	*arg;
	int skip = 0;

	if (node->pn_type == MMS_PN_OBJ) {
		if (node->pn_string != NULL) {

		skip = 0;
		if (mm_in_char_list(&cmd->cmd_dest_list,
		    node->pn_string)) {
			/* already have this as a dest */
			skip = 1;
		}
		if (!skip) {
			/* List way */
			if (mm_add_char(node->pn_string,
			    &cmd->cmd_dest_list)) {
				mms_trace(MMS_ERR, "Error adding "
				    "char to source list");
			}

			/* Array Way */

		cmd->cmd_dest_num ++;
		}

		}
		arg = mms_list_head(&node->pn_arglist);
	} else if (node->pn_flags & MMS_PN_MULTIOPS) {
		if (node->pn_flags & MMS_PN_REGEX) {
			/* sql regular expression is at end of list */
			arg = mms_list_head(&node->pn_arglist);
			mms_list_remove(&node->pn_arglist, arg);
			mms_list_insert_tail(&node->pn_arglist, arg);
		}
		mms_list_foreach(&node->pn_arglist, arg) {
			mm_get_dest_helper(arg, cmd);

		}
		if (node->pn_flags & MMS_PN_REGEX) {
			/* restore list to original order */
			arg = mms_list_tail(&node->pn_arglist);
			mms_list_remove(&node->pn_arglist, arg);
			mms_list_insert_head(&node->pn_arglist, arg);
		}
	} else if (node->pn_flags & MMS_PN_UNARYOPS) {
		if (strcmp(node->pn_string, "isattr") == 0 ||
		    strcmp(node->pn_string, "noattr") == 0) {
			arg = mms_list_head(&node->pn_arglist);

		} else {
			arg = mms_list_head(&node->pn_arglist);
			mm_get_dest_helper(arg, cmd);
		}
	}
}

void
mm_clear_const(mm_command_t *cmd) {
	mm_free_list(&cmd->cmd_const_list);
	cmd->cmd_const_num = 0;
}

void
mm_clear_dest(mm_command_t *cmd) {
	mm_free_list(&cmd->cmd_dest_list);
	cmd->cmd_dest_num = 0;
}
void
mm_clear_source(mm_command_t *cmd) {
	mm_free_list(&cmd->cmd_source_list);
	cmd->cmd_source_num = 0;
}



int
/* LINTED: mm_wka may be used in the future */
mm_get_dest(mm_wka_t *mm_wka, mm_command_t *cmd)
{

	mms_par_node_t	*match;
	mms_par_node_t	*node;
	mms_par_node_t	*vol_name;

	mms_trace(MMS_DEVP, "mm_get_dest");

	match = mms_pn_lookup(cmd->cmd_root, "match",
	    MMS_PN_CLAUSE, NULL);
	if (match == NULL) {
		vol_name = mms_pn_lookup(cmd->cmd_root, "volname",
		    MMS_PN_CLAUSE, NULL);
		if (vol_name != NULL) {
			/* List way */
			if (mm_add_char("VOLUME",
			    &cmd->cmd_dest_list)) {
				mms_trace(MMS_ERR, "Error adding "
				    "char to source list");
			}

			/* Array Way */

			cmd->cmd_dest_num ++;
			return (0);
		}
		return (1);

	}
	mms_list_foreach(&match->pn_arglist, node) {
		mm_get_dest_helper(node, cmd);
	}
	/* List TEST */
	mms_trace(MMS_DEVP, "Printing char_list dest...");
	mm_print_char_list(&cmd->cmd_dest_list);
	return (0);

}

int
mm_non_priv_const(mm_wka_t *mm_wka, mm_command_t *cmd) {

	char		*const_buf = NULL;
	mms_list_t		*source_list = &cmd->cmd_source_list;
	cci_t		*conn = &mm_wka->wka_conn;
	char		*app_name = conn->cci_client;


	/*
	 * Cartridge/volume constriants
	 */
	/* Check for the objects in the source list */
	if (mm_in_char_list(source_list, "CARTRIDGE") ||
	    mm_in_char_list(source_list, "PARTITION") ||
	    mm_in_char_list(source_list, "SIDE") ||
	    mm_in_char_list(source_list, "SLOT") ||
	    mm_in_char_list(source_list, "CARTRIDGEGROUP") ||
	    mm_in_char_list(source_list, "CARTRIDGEGROUPAPPLICATION")) {
		/* Add constraint for CARTRIDGE */
		(void) mm_add_to_dest(cmd,
			"CARTRIDGEGROUPAPPLICATION");
		if (const_buf)
			free(const_buf);
		const_buf = NULL;
		const_buf = mms_strapp(const_buf,
			"\"CARTRIDGEGROUPAPPLICATION\"."
			"\"ApplicationName\" = '%s'",
			app_name);
		(void) mm_add_to_const(cmd, const_buf);
	}
	if (mm_in_char_list(source_list, "VOLUME")) {
		/* Add constraint for VOLUME */
		(void) mm_add_to_dest(cmd,
			"VOLUME");
		if (const_buf)
			free(const_buf);
		const_buf = NULL;
		const_buf = mms_strapp(const_buf,
			"\"VOLUME\"."
			"\"ApplicationName\" = '%s'",
			app_name);
		(void) mm_add_to_const(cmd, const_buf);
	}


	/*
	 * Drive/Mount constraints
	 */
	if (mm_in_char_list(source_list, "DRIVE") ||
	    mm_in_char_list(source_list, "DRIVEGROUP") ||
	    mm_in_char_list(source_list, "DRIVEGROUPAPPLICATION") ||
	    mm_in_char_list(source_list, "DM") ||
	    mm_in_char_list(source_list, "DMCAPABILITY") ||
	    mm_in_char_list(source_list, "DMCAPABILITYTOKEN") ||
	    mm_in_char_list(source_list, "DMCAPABILITYDEFAULTTOKEN") ||
	    mm_in_char_list(source_list, "DMCAPABILITYGROUP") ||
	    mm_in_char_list(source_list, "DMCAPABILITYGROUPTOKEN") ||
	    mm_in_char_list(source_list, "DMBITFORMAT") ||
	    mm_in_char_list(source_list, "DMBITFORMATTOKEN")) {
		/* Add constraint for DRIVE */
		(void) mm_add_to_dest(cmd,
			"DRIVEGROUPAPPLICATION");
		if (const_buf)
			free(const_buf);
		const_buf = NULL;
		const_buf = mms_strapp(const_buf,
			"\"DRIVEGROUPAPPLICATION\"."
			"\"ApplicationName\" = '%s'",
			app_name);
		(void) mm_add_to_const(cmd, const_buf);
	}
	if (mm_in_char_list(source_list, "MOUNTPHYSICAL") ||
	    mm_in_char_list(source_list, "MOUNTLOGICAL") ||
	    mm_in_char_list(source_list, "STALEHANDLE")) {
		/* Add constraint for MOUNTPHYSICAL */
		(void) mm_add_to_dest(cmd,
			"MOUNTPHYSICAL");
		if (const_buf)
			free(const_buf);
		const_buf = NULL;
		const_buf = mms_strapp(const_buf,
			"\"MOUNTPHYSICAL\"."
			"\"ApplicationName\" = '%s'",
			app_name);
		(void) mm_add_to_const(cmd, const_buf);
	}
	/*
	 * System/connection constraints
	 */

	if (mm_in_char_list(source_list, "APPLICATION") ||
	    mm_in_char_list(source_list, "AI")) {
		/* Add constraint for APPLICATION */
		(void) mm_add_to_dest(cmd,
			"APPLICATION");
		if (const_buf)
			free(const_buf);
		const_buf = NULL;
		const_buf = mms_strapp(const_buf,
			"\"APPLICATION\"."
			"\"ApplicationName\" = '%s'",
			app_name);
		(void) mm_add_to_const(cmd, const_buf);
	}

	if (mm_in_char_list(source_list, "CONNECTION") ||
	    mm_in_char_list(source_list, "SESSION") ||
	    mm_in_char_list(source_list, "NOTIFY")) {
		/* Add constraint for DRIVE */
		(void) mm_add_to_dest(cmd,
			"CONNECTION");
		if (const_buf)
			free(const_buf);
		const_buf = NULL;
		const_buf = mms_strapp(const_buf,
			"\"CONNECTION\"."
			"\"ConnectionClientName\" = '%s'",
			app_name);
		(void) mm_add_to_const(cmd, const_buf);
	}
	if (const_buf)
		free(const_buf);
	const_buf = NULL;
	return (0);
}

void
mm_path_match_report(mm_command_t *cmd, mm_db_t *db)
{

	int		  off;
	mms_par_node_t	 *report;
	mms_par_node_t	 *reportmode;
	mms_par_node_t	 *object;
	mms_par_node_t	 *attr;
	mms_par_node_t	*work;
	mm_wka_t *mm_wka = cmd->wka_ptr;

	mms_trace(MMS_DEVP, "mm_path_match_report");

	report = mms_pn_lookup(cmd->cmd_root, "report",
	    MMS_PN_CLAUSE, NULL);
	if (report == NULL) {
		SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize,
		    strlen(RESPONSE_SUCCESS) +
		    strlen(cmd->cmd_task) + 1);
		(void) snprintf(cmd->cmd_buf, cmd->cmd_bufsize,
		    RESPONSE_SUCCESS, cmd->cmd_task);
		return;
	}

	mm_clear_source(cmd);
	if (mm_get_source(mm_wka, cmd)) {
		/* Command does not have a report clause */
		mms_trace(MMS_DEVP, "No report clause");
	} else {
		/* TEMP - Trace out our source info */
		mms_trace(MMS_DEVP, "Source count is %d", cmd->cmd_source_num);
		mm_print_char_list(&cmd->cmd_source_list);
	}

	mm_clear_dest(cmd);
	if (mm_get_dest(mm_wka, cmd)) {
		/* Command does not have a match/volname clause */
		mms_trace(MMS_DEVP, "No match/volname Clause");
	} else {
		/* TEMP - Trace out our dest info */
		mms_trace(MMS_DEVP, "Dest count is %d", cmd->cmd_dest_num);
		mm_print_char_list(&cmd->cmd_dest_list);
	}

	/* Build Constraint List */
	mm_clear_const(cmd);
	if (mm_get_const(mm_wka, cmd)) {
		/* Command does not have a match/volname clause */
		mms_trace(MMS_DEVP, "No match/volname Clause???");
	} else {
		/* TEMP - Trace out our const info */
		mms_trace(MMS_DEVP, "Const count is %d", cmd->cmd_const_num);
		mm_print_char_list(&cmd->cmd_const_list);
	}

	/* For non priv-clients, check the source list */
	/* Add any additional constraits necessary */
	/* ie. VOLUME.ApplicationName = cci_client */
	/* both source + dest may need adds */
	if (cmd->wka_ptr->wka_privilege == MM_PRIV_STANDARD) {
		mms_trace(MMS_DEVP,
		    "add non-priv constraints");
		/* Need to check source list and */
		/* add constraints as necessary */
		if (mm_non_priv_const(mm_wka, cmd)) {
			mms_trace(MMS_ERR,
			    "error adding non-priv "
			    "client constraints");
		}
		mms_trace(MMS_DEVP,
		    "const list after additions, %d",
		    cmd->cmd_const_num);
		mm_print_char_list(&cmd->cmd_const_list);
	} else {
		mms_trace(MMS_DEVP,
		    "skip constraints forpriv client");
	}

	SQL_CHK_LEN(&cmd->cmd_buf, 0, &cmd->cmd_bufsize, 7);
	off = snprintf(cmd->cmd_buf, cmd->cmd_bufsize, "SELECT ");

	/* If unique is specified in the reportmode clause, insert DISTINCT */
	reportmode = mms_pn_lookup(cmd->cmd_root, "reportmode",
	    MMS_PN_CLAUSE, NULL);
	if (reportmode && mms_pn_lookup(reportmode, "unique",
	    MMS_PN_KEYWORD, NULL)) {
		SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize, 9);
		off += snprintf(cmd->cmd_buf + off, cmd->cmd_bufsize - off,
		    "DISTINCT ");
	}
	work = NULL;
	for (object = mms_pn_lookup(report, NULL, MMS_PN_OBJ, &work);
	    object != NULL;
	    object = mms_pn_lookup(report, NULL, MMS_PN_OBJ, &work)) {
		attr = mms_pn_lookup(object, NULL, MMS_PN_ATTR, NULL);
		if (attr == NULL) {
			/* show all attributes */
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    4 + strlen(object->pn_string));
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off,
			    "\"%s\".*,", object->pn_string);
		} else {
			/*
			 * More than one object column reference can't be be
			 * ambiguous, specify object and attribute.
			 */
			SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize,
			    6 + strlen(object->pn_string) +
			    strlen(object->pn_string) +
			    strlen(attr->pn_string));
			off += snprintf(cmd->cmd_buf + off,
			    cmd->cmd_bufsize - off,
			    "\"%s\".\"%s\",",
			    object->pn_string,
			    attr->pn_string);
		}
	}
	off--;			/* overwrite the last comma */
	SQL_CHK_LEN(&cmd->cmd_buf, off, &cmd->cmd_bufsize, 6);
	off += snprintf(cmd->cmd_buf + off, cmd->cmd_bufsize - off, " FROM ");

	if (mm_sql_from_where(cmd, db)) {
		mms_trace(MMS_ERR,
		    "mm_path_match_report: "
		    "db error creating helper functions");
		return;
	}

	mm_sql_order(cmd);
	mm_sql_number(cmd);


	if (mm_sql_report(cmd)) {
		mms_trace(MMS_ERR,
		    "mm_path_match_report: "
		    "error generating final response");
	}
	return;

no_mem:
	MM_ABORT_NO_MEM();
	return;

not_found:
	mm_response_error(cmd,
	    ECLASS_LANGUAGE,
	    ENOTFOUND,
	    MM_5062_MSG,
	    NULL);
	return;

}

/*
 * function to set a LM/DM/DRIVE/LIBRARY state
 * uses its own db connection
 * to set drive 1, DRIVE.DriveStateHard to 'loaded' :
 * mm_sql_update_state(&mm_data, "DRIVE", "DriveStateHard", "loaded",
 *		"DriveName", "drive1");
 */
#define	MM_SQL_UPDATE_STATE "update \"%s\" "\
	"set \"%s\" = '%s' "\
	"where \"%s\" = '%s';"
void
mm_sql_update_state(mm_data_t *data, char *object, char *attribute,
		    char *value, char *instance, char *name)
{

	mm_db_t		*db = &data->mm_db;
	if (mm_db_exec(HERE, db, MM_SQL_UPDATE_STATE,
	    object, attribute, value,
	    instance, name) != MM_DB_OK) {
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		mms_trace(MMS_ERR,
		    "mm_sql_update_state: "
		    "db error updating db states");
	}
	return;

}


int
/* LINTED: db is used by this function */
mm_system_settings(mm_db_t *db, int *request_oper, int *auto_clear) {


#if 1
	/* TEMP - dont request, always clear */
	*auto_clear = 1;
	*request_oper = 0;
	return (0);
#else
	rc = mm_db_exec(HERE, db,
			"select "
			"\"AskClearDriveAtLMConfig\","
			"\"ClearDriveAtLMConfig\" "
			"from \"SYSTEM\";");
	if (rc != MM_DB_DATA) {
		mms_trace(MMS_ERR, "Db error in mm_clear_unknown_tape");
		mm_clear_db(&db->mm_db_results);
		db->mm_db_results = NULL;
		return (1);
	}
	if (PQntuples(db->mm_db_results) < 1) {
		mms_trace(MMS_ERR, "Missing SYSTEM object");
		return (1);

	}
	mms_trace(MMS_DEVP, "AskClearDrive = %s, ClearDrive = %s",
	    PQgetvalue(db->mm_db_results, 0, 0),
	    PQgetvalue(db->mm_db_results, 0, 1));
	if (strcmp(PQgetvalue(db->mm_db_results, 0, 0),
		    "yes") == 0) {
		*request_oper = 1;

	}
	/* Don't ask the oper */
	if (strcmp(PQgetvalue(db->mm_db_results, 0, 1),
		    "yes") == 0) {
		*auto_clear = 1;
	}
	return (0);
#endif
}

int
/* LINTED: a mm_wka arg is required by all cmd functions */
mm_req_test_cmd_func(mm_wka_t *mm_wka, mm_command_t *cmd) {
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;
	mms_trace(MMS_DEVP, "mm_req_test_cmd_func");
	mms_trace(MMS_DEVP, "request clear for %s, %s",
	    mount_info->cmi_drive, mount_info->cmi_cartridge);
	return (MM_DEPEND_DONE);

}



int
mm_add_clear_request(mm_wka_t *mm_wka, mm_command_t *cmd) {
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;
	mm_command_t		*req_cmd;
	mm_wka_t		*oper_wka;
	mm_data_t		*mm_data = mm_wka->mm_data;
	uuid_text_t		uuid;

	mms_trace(MMS_DEVP, "adding clear drive request,"
	    "drive %s, cartridge %s",
	    mount_info->cmi_drive, mount_info->cmi_cartridge);


	oper_wka = NULL;
	mms_list_foreach(&mm_data->mm_wka_list, oper_wka) {
		if ((strcmp(oper_wka->wka_conn.cci_instance,
			    "oper") == 0) &&
		    (strcmp(oper_wka->wka_conn.cci_client,
			    "MMS") == 0)) {
			/* Found the wka of oper */
			break;
		}
	}
	if (oper_wka == NULL) {
		mms_trace(MMS_DEBUG, "operator not connected");
		return (1);
	}

	if ((req_cmd = mm_alloc_cmd(oper_wka)) == NULL) {
		mms_trace(MMS_ERR,
			"Unable to malloc mm_command_t: %s",
			strerror(errno));
		return (1);
	}
	mm_add_depend(req_cmd, cmd);
	mm_get_uuid(uuid);
	req_cmd->cmd_root = NULL;
	req_cmd->cmd_task = NULL;
	req_cmd->cmd_task = strdup(uuid);
	if (req_cmd->cmd_task == NULL) {
		mms_trace(MMS_ERR, "Error malloc cmd_task in add cmd");
		return (MM_CMD_ERROR);
	}
	req_cmd->cmd_func = mm_req_test_cmd_func;
	req_cmd->cmd_name = strdup("clear drive oper request");

	req_cmd->cmd_mount_info.cmi_drive = strdup(mount_info->cmi_drive);
	req_cmd->cmd_mount_info.cmi_cartridge =
		strdup(mount_info->cmi_cartridge);
	mms_list_insert_tail(&mm_data->mm_cmd_queue, req_cmd);
	return (0);

}
extern int mm_errorcode_eq(mms_par_node_t *cmd_response, char *code) {

	/* This function takes an MMP/DMP/LMP */
	/* response and returns a pointer to the error code */

	mm_response_t		response;


	if (mm_parse_response(cmd_response, &response) == 1) {
		mms_trace(MMS_ERR,
		    "Error parsing command response");
		return (0);
	}
	if ((response.error_class == NULL) ||
	    (response.error_code == NULL)) {
		/* Might not be an error response */
		return (0);
	}
	if (strcmp(response.error_code, code) == 0) {
		return (1);
	}
	return (0);

}
int
mm_non_physical_clear_cmd_func(mm_wka_t *mm_wka, mm_command_t *cmd) {
	mm_db_t			*db = &cmd->cmd_mm_data->mm_db;
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;

	/* 1. DMP detach */
	/* 2. DMP release */
	/* 3. Schedule delay unmount/unload */

	if (cmd->cmd_state == 0) {
		mms_trace(MMS_DEVP,
		    "non-physical clear, add detach");
		/* Need both drive and dm set */
		if ((mount_info->cmi_drive == NULL)) {
			mms_trace(MMS_ERR,
			    "cmi_drive/dm is NULL for "
			    "this non-physical clear");
			return (MM_CMD_ERROR);
		}
		/* Get DM Name */
		if (mm_db_exec(HERE, db,
		    "select \"DMName\" from \"DRIVE\" "
		    "where \"DRIVE\".\"DriveName\" = '%s';",
		    mount_info->cmi_drive) != MM_DB_DATA) {
			mms_trace(MMS_ERR,
			    "db error");
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			return (MM_CMD_ERROR);
		}
		if (PQntuples(db->mm_db_results) != 1) {
			mms_trace(MMS_ERR,
			    "row num mismatch");
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}
		if (strcmp(PQgetvalue(db->mm_db_results, 0, 0), "") == 0) {
			mms_trace(MMS_ERR,
			    "missing dm name");
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}
		mm_set_mount_info_dm(PQgetvalue(db->mm_db_results, 0, 0),
				mount_info);
		mm_clear_db(&db->mm_db_results);

		/* Send a detach */
		if (mm_dmp_add_cmd(mm_wka, cmd, mount_info->cmi_dm,
		    MM_DMP_DETACH) == NULL) {
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "error adding dmp detach");
		}
		cmd->cmd_state = 1;
		return (MM_DISPATCH_DEPEND);

	}
	if (cmd->cmd_state == 1) {
		mms_trace(MMS_DEVP,
		    "non-physical clear, add release");
		/*
		 * Detach has returned
		 * check error, update states
		 * Add Unload command
		 */
		if (cmd->cmd_flags & MM_CMD_DEPEND_ERROR) {
			if ((cmd->cmd_response != NULL) &&
			    mm_errorcode_eq(cmd->cmd_response,
					"DM_E_NOEXISTHANDLE")) {
				mms_trace(MMS_DEVP,
				    "DM doesn't know the handle, "
				    "skip DMP unload, "
				    "send LMP unmount");
				/* Clear depend error flags */
				MM_UNSET_FLAG(cmd->cmd_flags,
					MM_CMD_DEPEND_ERROR);
			} else {
				/* detach returned handle in use */
				mms_trace(MMS_ERR,
				    "non-physical clear_drive -> "
				    "DMP detach error, "
				    "handle may be inuse");
				return (MM_CMD_ERROR);
			}

		}
		mms_trace(MMS_DEVP,
		    "DM is not using handle, send DMP release");
		if (mm_dmp_add_cmd(mm_wka, cmd, mount_info->cmi_dm,
		    MM_DMP_RELEASE) == NULL) {
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "error adding dmp release");
		}
		cmd->cmd_state = 2;
		return (MM_DISPATCH_DEPEND);
	}
	if (cmd->cmd_state == 2) {
		mms_trace(MMS_DEVP,
		    "non-physical clear, schedule unload");
		if (cmd->cmd_flags & MM_CMD_DEPEND_ERROR) {
			/* unload returned an error */
			mms_trace(MMS_ERR, "clear_drive -> DMP release error");
		}
		if (mm_db_exec(HERE, db,
		    "select distinct \"CARTRIDGE\"."
		    "\"CartridgeID\" from \"CARTRIDGE\""
		    "cross join \"DRIVE\""
		    "where"
		    "((\"CARTRIDGE\".\"LibraryName\" "
		    "= \"DRIVE\".\"LibraryName\")"
		    "and"
		    "(\"DRIVE\".\"DriveName\" = '%s')"
		    "and"
		    "(\"CARTRIDGE\".\"CartridgePCL\" = "
		    "\"DRIVE\".\"CartridgePCL\"));",
		    mount_info->cmi_drive) != MM_DB_DATA) {
			mms_trace(MMS_ERR,
			    "db error");
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			return (MM_CMD_ERROR);
		}
		if (PQntuples(db->mm_db_results) != 1) {
			mms_trace(MMS_ERR,
			    "row num mismatch");
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}
		mm_set_mount_info_cart(PQgetvalue(db->mm_db_results, 0, 0),
				mount_info);
		mm_clear_db(&db->mm_db_results);

		if (mm_db_exec(HERE, db, "delete from \"TASKCARTRIDGE\""
		    "where \"TaskID\" = '%s';", cmd->cmd_uuid) != MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "db error deleting TASKCARTRIDGE");
		}
		if (mm_db_exec(HERE, db, "delete from \"TASKDRIVE\" "
		    "where \"TaskID\" = '%s';", cmd->cmd_uuid) != MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "db error deleting TASKDRIVE");
		}
		if (mm_db_exec(HERE, db, "delete from \"TASKLIBRARY\" "
		    "where \"TaskID\" = '%s';", cmd->cmd_uuid) != MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "db error deleting TASKLIBRARY");
		}
		if (mm_db_exec(HERE, db, "delete from \"TASK\" "
		    "where \"TaskID\" = '%s';", cmd->cmd_uuid) != MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "db error deleting TASK");
		}
		if (mm_db_exec(HERE, db, "delete from \"MOUNTLOGICAL\" "
		    "where \"CartridgeID\" = '%s';",
		    mount_info->cmi_cartridge) != MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "db error deleting MOUNTLOGICAL");
		}
		if (mm_db_exec(HERE, db, "delete from \"MOUNTPHYSICAL\" "
		    "where \"CartridgeID\" = '%s';",
		    mount_info->cmi_cartridge) != MM_DB_OK) {
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "db error deleting MOUNTPHYSICAL");
		}

		if (mm_schedule_unload(mm_wka, cmd)) {
			mms_trace(MMS_ERR,
			    "mm_non_physical_clear_cmd_func: "
			    "error scheduling delay unload");
		}
		cmd->cmd_remove = 1;
		mms_trace(MMS_DEVP,
		    "non-physical clear complete, delay unload scheduled");
		return (MM_DISPATCH_AGAIN);

	}
	mms_trace(MMS_ERR,
	    "mm_non_physical_clear_cmd_func: unknown cmd state");
	return (MM_CMD_ERROR);
}


#define	MM_CLEAR_START 0
#define	MM_CLEAR_SLOTSCAN 20
#define	MM_CLEAR_DETACH 21
#define	MM_CLEAR_RELEASE 22
#define	MM_CLEAR_UNLOAD 23
#define	MM_CLEAR_UNMOUNT 24
#define	MM_CLEAR_FINAL 25

int
mm_clear_drive_cmd_func(mm_wka_t *mm_wka, mm_command_t *cmd) {
	mm_db_t			*db = &cmd->cmd_mm_data->mm_db;
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;
	mm_data_t		*data = cmd->cmd_mm_data;
	int			rc;

	mm_command_t		*lmp_umnt_cmd;
	uuid_text_t		new_task;


	/* This is a physical unmount */
	/* detach, release, unload, unmount */
start:
	if (cmd->cmd_state == MM_CLEAR_START) {

		/* Clear depend error flags, */
		/* On the 1st cmd state of clear drive */
		/*  ignore any errors */
		/* This clear drive may be running after */
		/* A DMP activate which may have errored */
		/* Try to clear anyways */

		MM_UNSET_FLAG(cmd->cmd_flags,
		    MM_CMD_DEPEND_ERROR);

		if (mount_info->cmi_library == NULL) {
			mms_trace(MMS_ERR,
			    "library name not set for clear drive");
			return (MM_CMD_ERROR);
		}
		/* Confirm LM is connected and active */
		rc = mm_db_exec(HERE, db,
		    "select distinct "
		    "\"LM\".\"LMStateSoft\" from \"LM\""
		    "cross join \"LIBRARY\""
		    "where((\"LM\".\"LibraryName\" = "
		    "\"LIBRARY\".\"LibraryName\")"
		    "and(\"LIBRARY\".\"LibraryName\" = '%s'));",
		    mount_info->cmi_library);
		if (rc != MM_DB_DATA) {
			mms_trace(MMS_ERR, "Exec returned with no Data");
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			return (MM_CMD_ERROR);
		}
		if (PQntuples(db->mm_db_results) == 0) {
			mms_trace(MMS_ERR,
			    "library, %s, has no lm assigned",
			    mount_info->cmi_library);
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}

		if (strcmp(PQgetvalue(db->mm_db_results, 0, 0),
		    "not ready") == 0) {
			mms_trace(MMS_DEVP, "lm is not ready, wait");
			MM_SET_FLAG(cmd->cmd_flags, MM_CMD_DISPATCHABLE);
			mm_clear_db(&db->mm_db_results);
			return (MM_NO_DISPATCH);
		}
		if (strcmp(PQgetvalue(db->mm_db_results, 0, 0), "ready") == 0) {
			mms_trace(MMS_DEVP, "lm ready, continue");
		} else {
			mms_trace(MMS_ERR,
			    "lm state = %s, cancel clear drive",
			    PQgetvalue(db->mm_db_results, 0, 0));
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}
		mm_clear_db(&db->mm_db_results);

		mms_trace(MMS_DEBUG, "LM Ready, try to clear "
		    "drive -> %s, send scan",
		    mount_info->cmi_drive);

		/* Add LMP scan command for this drive */
		if (mm_add_lmp_scan(mm_wka->mm_data, cmd,
				    mount_info->cmi_drive,
				    NULL, mount_info->cmi_library)) {
			mms_trace(MMS_ERR,
			    "Error adding LMP scan");
			return (MM_CMD_ERROR);
		} else {
			mms_trace(MMS_DEBUG,
			    "Added LMP scan");
			cmd->cmd_state = MM_CLEAR_DETACH;

		}
		/* If DRIVE is occupied and has pcl set */
		/* MM will need to send lmp scan for the */
		/* cartridge as well */

		if (mount_info->cmi_pcl != NULL) {
			mms_trace(MMS_DEVP,
			    "cmi_pcl already set, %s",
			    mount_info->cmi_pcl);
			cmd->cmd_state = MM_CLEAR_SLOTSCAN;
			return (MM_DISPATCH_DEPEND);
		}

		/* set mount_info->cmi_pcl */
		if (mm_db_exec(HERE, db,
		    "select distinct "
		    "\"DRIVE\".\"CartridgePCL\""
		    "from \"DRIVE\" "
		    "where"
		    " \"DRIVE\".\"DriveName\" = '%s'; ",
		    mount_info->cmi_drive) != MM_DB_DATA) {
			mms_trace(MMS_ERR, "Couldn't get Cartridge_PCL -> "
			    "mm_clear_drive_cmd_func");
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			return (MM_CMD_ERROR);
		}
		if (PQntuples(db->mm_db_results) == 0) {
			mms_trace(MMS_ERR,
			    "error getting pcl from drive");
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}
		if (strcmp(PQgetvalue(db->mm_db_results,
		    0, 0), MM_NON_MMS_CART) == 0) {
			mms_trace(MMS_DEVP,
			    "drive loaded with non-mms cart");
			/* If non-mms cart return */
			/* since this is not part of MMS */
			/* MM does not need a cart scan */
			mm_clear_db(&db->mm_db_results);
			return (MM_DISPATCH_DEPEND);
		}
		if (strcmp(PQgetvalue(db->mm_db_results,
		    0, 0), "") != 0) {
			/* DRIVE.pcl was set */
			cmd->cmd_state = MM_CLEAR_SLOTSCAN;
			mm_set_mount_info_pcl(PQgetvalue(db->
			    mm_db_results, 0, 0),
			    mount_info);
		}
		mm_clear_db(&db->mm_db_results);

		return (MM_DISPATCH_DEPEND);
	}

	if (cmd->cmd_state == MM_CLEAR_SLOTSCAN) {
		/* Command state when a lmp scan for slot is needed */
		/* Drive scan has completed */
		mms_trace(MMS_DEVP,
		    "drive scan complete, send scan of PCL");

		/* Add LMP scan command for this drive */
		if (mm_add_lmp_scan(mm_wka->mm_data, cmd,
		    NULL, mount_info->cmi_pcl,
		    mount_info->cmi_library)) {
			mms_trace(MMS_ERR,
			    "Error adding LMP scan");
			return (MM_CMD_ERROR);
		} else {
			mms_trace(MMS_DEBUG,
			    "Added LMP scan");
			cmd->cmd_state = MM_CLEAR_DETACH;

		}
		if (mount_info->cmi_pcl != NULL) {
			free(mount_info->cmi_pcl);
			mount_info->cmi_pcl = NULL;
		}
		return (MM_DISPATCH_DEPEND);
	}

	if (cmd->cmd_state == MM_CLEAR_DETACH) {
		/* Send the DMP detach command */
		mms_trace(MMS_DEVP,
		    "physical clear, add detach");
		/* Need drive set */
		if ((mount_info->cmi_drive == NULL)) {
			mms_trace(MMS_ERR,
			    "cmi_drive is NULL for "
			    "this physical clear");
			return (MM_CMD_ERROR);
		}

		/* set mount_info->cmi_pcl */
		if (mm_db_exec(HERE, db,
		    "select distinct "
		    "\"DRIVE\".\"CartridgePCL\""
		    "from \"DRIVE\" "
		    "where"
		    " \"DRIVE\".\"DriveName\" = '%s'; ",
		    mount_info->cmi_drive) != MM_DB_DATA) {
			mms_trace(MMS_ERR, "Couldn't get Cartridge_PCL -> "
			    "mm_clear_drive_cmd_func");
			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			return (MM_CMD_ERROR);
		}

		if (PQntuples(db->mm_db_results) == 0) {
			mms_trace(MMS_ERR,
			    "error getting pcl from drive");
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}

		if (strcmp(PQgetvalue(db->mm_db_results,
		    0, 0), "") == 0) {
			mms_trace(MMS_DEVP,
			    "LMP scan of drive shows %s is clear",
			    mount_info->cmi_drive);

			mms_trace(MMS_INFO, "%s Cleared Successfully",
			    mount_info->cmi_drive);

			/* Set States For a Clear Drive */
			if (mm_db_exec(HERE, db,
			    "update \"DRIVE\" set "
			    "\"DriveStateHard\" = 'unloaded', "
			    "\"DMName\" = DEFAULT, "
			    "\"DriveLibraryOccupied\" = 'f' "
			    "where \"DriveName\" = '%s';",
			    mount_info->cmi_drive) != MM_DB_OK) {
				mm_clear_db(&db->mm_db_results);
				db->mm_db_results = NULL;
				mms_trace(MMS_ERR,
				    "mm_clear_drive_cmd_func: "
				    "db error updating DRIVE");
			}

			/* Delete any mountphysical, mountlogical, */
			/* or stalehandle */
			if (mm_db_exec(HERE, db,
			    "delete from \"MOUNTLOGICAL\" "
			    "where \"DriveName\" = '%s';",
			    mount_info->cmi_drive) != MM_DB_OK) {
				mm_clear_db(&db->mm_db_results);
				db->mm_db_results = NULL;
				mms_trace(MMS_ERR,
				    "mm_clear_drive_cmd_func: "
				    "db error deleting MOUNTLOGICAL");
			}
			if (mm_db_exec(HERE, db,
			    "delete from \"STALEHANDLE\" "
			    "where \"DriveName\" = '%s';",
			    mount_info->cmi_drive) != MM_DB_OK) {
				mm_clear_db(&db->mm_db_results);
				db->mm_db_results = NULL;
				mms_trace(MMS_ERR,
				    "mm_clear_drive_cmd_func: "
				    "db error deleting STALEHANDLE");
			}
			if (mm_db_exec(HERE, db,
			    "delete from \"MOUNTPHYSICAL\" "
			    "where \"DriveName\" = '%s';",
			    mount_info->cmi_drive) != MM_DB_OK) {
				mm_clear_db(&db->mm_db_results);
				db->mm_db_results = NULL;
				mms_trace(MMS_ERR,
				    "mm_clear_drive_cmd_func: "
				    "db error deleting MOUNTPHYSICAL");
			}

			mm_clear_db(&db->mm_db_results);
			db->mm_db_results = NULL;
			if (mm_has_depend(cmd)) {
				return (MM_DEPEND_DONE);
			}
			return (MM_CMD_DONE);

		}

		if (strcmp(PQgetvalue(db->mm_db_results,
		    0, 0), MM_NON_MMS_CART) == 0) {
			mms_trace(MMS_DEVP,
			    "this is a clear drive for a non-mms cartridge");
			/* Look in the response to the scan to */
			/* find the pcl in this drive */
			if (mount_info->cmi_pcl != NULL) {
				mms_trace(MMS_DEVP,
				    "pcl already set to %s",
				    mount_info->cmi_pcl);
			} else {
				mms_trace(MMS_ERR,
				    "pcl is NULL for a non-MMS cart");
				mm_clear_db(&db->mm_db_results);
				return (MM_CMD_ERROR);
			}
		} else {
			mm_set_mount_info_pcl(PQgetvalue(db->
			    mm_db_results, 0, 0),
			    mount_info);
		}
		mm_clear_db(&db->mm_db_results);
		if (mount_info->cmi_pcl == NULL) {
			mms_trace(MMS_ERR,
			    "pcl is NULL ");
			return (MM_CMD_ERROR);
		}
		mms_trace(MMS_DEVP,
		    "pcl set to %s",
		    mount_info->cmi_pcl);
		/* Get DM Name */
		if (mm_db_exec(HERE, db,
		    "select \"DMName\" from \"DRIVE\" "
		    "where \"DRIVE\".\"DriveName\" = '%s';",
		    mount_info->cmi_drive) != MM_DB_DATA) {
			mms_trace(MMS_ERR,
			    "db error");
			return (MM_CMD_ERROR);
		}
		if (PQntuples(db->mm_db_results) != 1) {
			mms_trace(MMS_ERR,
			    "row num mismatch");
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}
		if (strcmp(PQgetvalue(db->mm_db_results, 0, 0), "") == 0) {
			mms_trace(MMS_ERR,
			    "missing dm name in DRIVE, "
			    "dm may already be detached, check STALEHANDLE");
			mm_clear_db(&db->mm_db_results);
			/* Try getting DMName from STALEHANDLE */
			if (mm_db_exec(HERE, db,
			    "select \"DMName\" from \"STALEHANDLE\" "
			    "where \"STALEHANDLE\".\"DriveName\" = '%s';",
			    mount_info->cmi_drive) != MM_DB_DATA) {
				mms_trace(MMS_ERR,
				    "db error");
				return (MM_CMD_ERROR);
			}
			if (PQntuples(db->mm_db_results) != 1) {
				mms_trace(MMS_ERR,
				    "Can't find STALEHANDLE, continue unmount");
				mm_clear_db(&db->mm_db_results);
				cmd->cmd_state = MM_CLEAR_UNLOAD;
				goto start;
			}
		}
		mm_set_mount_info_dm(PQgetvalue(db->mm_db_results, 0, 0),
				mount_info);
		mm_clear_db(&db->mm_db_results);

		/* Send a detach */
		if (mm_dmp_add_cmd(mm_wka, cmd, mount_info->cmi_dm,
		    MM_DMP_DETACH) == NULL) {
			mms_trace(MMS_ERR,
			    "mm_clear_drive_cmd_func: "
			    "error adding dmp detach");
		}
		cmd->cmd_state = MM_CLEAR_RELEASE;
		return (MM_DISPATCH_DEPEND);
	}

	if (cmd->cmd_state == MM_CLEAR_RELEASE) {
		/* Send the DMP release command */
		mms_trace(MMS_DEVP,
		    "physical clear, add release");
		/*
		 * Detach has returned
		 * check error, update states
		 * Add Unload command
		 */
		if (cmd->cmd_flags & MM_CMD_DEPEND_ERROR) {
			if ((cmd->cmd_response != NULL) &&
			    mm_errorcode_eq(cmd->cmd_response,
					"DM_E_NOEXISTHANDLE")) {
				mms_trace(MMS_DEVP,
				    "DM doesn't know the handle, "
				    "skip DMP unload, "
				    "send LMP unmount");
				/* Clear depend error flags */
				MM_UNSET_FLAG(cmd->cmd_flags,
					MM_CMD_DEPEND_ERROR);
			} else {
				/* detach returned handle in use */
				mms_trace(MMS_ERR, "physical clear_drive -> "
				    "DMP detach error, "
				    "handle may be inuse");
				return (MM_CMD_ERROR);
			}

		}
		mms_trace(MMS_DEVP,
		    "DM is not using handle, send DMP release if dm is ready");

		/* Check, if DM is not activated, don't send a release */
		if (mm_db_exec(HERE, db,
		    "select \"DMStateSoft\" from \"DM\" "
		    "where \"DM\".\"DMName\" = '%s';",
		    mount_info->cmi_dm) != MM_DB_DATA) {
			mms_trace(MMS_ERR,
			    "db error");
			return (MM_CMD_ERROR);
		}
		if (PQntuples(db->mm_db_results) != 1) {
			mms_trace(MMS_ERR,
			    "Can't find DM, continue unmount");
			mm_clear_db(&db->mm_db_results);
			return (MM_CMD_ERROR);
		}
		if ((strcmp(PQgetvalue(db->mm_db_results, 0, 0),
		    "present") == 0) ||
		    (strcmp(PQgetvalue(db->mm_db_results, 0, 0),
		    "absent") == 0) ||
		    (strcmp(PQgetvalue(db->mm_db_results, 0, 0),
		    "disconnected") == 0)) {
			/* DM cannot process release */
			/* Continue to unmount */
			mms_trace(MMS_DEVP,
			    "DMStateSoft = %s, don't send release, "
			    "continue with unmount",
			    PQgetvalue(db->mm_db_results, 0, 0));
			    mm_clear_db(&db->mm_db_results);
			    cmd->cmd_state = MM_CLEAR_UNLOAD;
			    goto start;
		}
		mms_trace(MMS_DEVP,
		    "DMStateSoft = %s, about to send release",
		    PQgetvalue(db->mm_db_results, 0, 0));
		mm_clear_db(&db->mm_db_results);
		if (mm_dmp_add_cmd(mm_wka, cmd, mount_info->cmi_dm,
		    MM_DMP_RELEASE) == NULL) {
			mms_trace(MMS_ERR,
			    "mm_clear_drive_cmd_func: "
			    "error adding dmp release");
		}
		cmd->cmd_state = MM_CLEAR_UNLOAD;
		return (MM_DISPATCH_DEPEND);
	}
	if (cmd->cmd_state == MM_CLEAR_UNLOAD) {

		/* Always send a DMP unload before the physicalunmount */
		/* If MM knows what DM to send to */

		/* Try to send an unload now */
		MM_UNSET_FLAG(cmd->cmd_flags,
		    MM_CMD_DEPEND_ERROR);

		if (mount_info->cmi_dm == NULL) {
			mms_trace(MMS_ERR,
			    "didn't find DM Name, "
			    "skip DMP unload");
			cmd->cmd_state = MM_CLEAR_UNMOUNT;
			goto start;
		}
		mms_trace(MMS_DEVP,
		    "sending %s DMP unload",
		    mount_info->cmi_dm);
		if (mm_dmp_add_cmd(mm_wka, cmd, mount_info->cmi_dm,
		    MM_DMP_UNLOAD) == NULL) {
			mms_trace(MMS_ERR,
			    "mm_clear_drive_func: "
			    "error adding dmp unload");
		}
		cmd->cmd_state = MM_CLEAR_UNMOUNT;
		return (MM_DISPATCH_DEPEND);
	}

	if (cmd->cmd_state == MM_CLEAR_UNMOUNT) {
		/* DMP unload has completed, ignore any errors */
		/* and continue with the LMP unmount */

		if (cmd->cmd_flags & MM_CMD_DEPEND_ERROR) {
			mms_trace(MMS_ERR,
			    "error during DMP unload, "
			    "continue with LMP unmount");
			/* Clear depend error flags */
			MM_UNSET_FLAG(cmd->cmd_flags,
			    MM_CMD_DEPEND_ERROR);
		}
		mms_trace(MMS_DEVP,
		    "physical clear, add unmount");
		mms_trace(MMS_INFO, "Unmounting %s from %s",
		    mount_info->cmi_pcl,
		    mount_info->cmi_drive);

		/*
		 * LMP unmount command
		 */

		/* ********************* */
		/* TODO: FIX SIDE NAME!! */
		if ((lmp_umnt_cmd = mm_alloc_cmd(mm_wka)) == NULL) {
			mms_trace(MMS_ERR,
			    "Unable to malloc mm_command_t: %s",
			    strerror(errno));
			return (MM_CMD_ERROR);
		}
		lmp_umnt_cmd->cmd_func = mm_lmp_unmount_cmd_func;
		mm_get_uuid(new_task);
		lmp_umnt_cmd->cmd_textcmd = mms_strnew(LMP_UNMOUNT, new_task,
						mount_info->cmi_pcl,
						mount_info->cmi_pcl,
						"side 1",
						mount_info->cmi_drive,
						mount_info->cmi_pcl,
						mount_info->cmi_pcl,
						"side 1");
		mm_set_mount_info_drive(cmd->cmd_mount_info.cmi_drive,
		    &lmp_umnt_cmd->cmd_mount_info);
		lmp_umnt_cmd->cmd_root =
			mm_text_to_par_node(lmp_umnt_cmd->cmd_textcmd,
					    mms_lmpm_parse);
		lmp_umnt_cmd->cmd_task = mm_get_task(lmp_umnt_cmd->cmd_root);
		mm_add_depend(lmp_umnt_cmd, cmd);
		lmp_umnt_cmd->cmd_name = strdup("lmp unmount");

		pthread_mutex_lock(&data->mm_queue_mutex);
		mms_list_insert_tail(&data->mm_cmd_queue, lmp_umnt_cmd);
		pthread_mutex_unlock(&data->mm_queue_mutex);

		cmd->cmd_state = MM_CLEAR_FINAL;
		return (MM_DISPATCH_DEPEND);
	}
	if (cmd->cmd_state == MM_CLEAR_FINAL) {
		/*
		 * LMP mount has returned
		 * check for error,
		 * Return MM_DEPEND_DONE on success
		 */
		if (cmd->cmd_flags & MM_CMD_DEPEND_ERROR) {
			return (MM_CMD_ERROR);
		}
		mms_trace(MMS_INFO, "%s Cleared Successfully",
		    mount_info->cmi_drive);

		/* Set States For a Clear Drive */
		if (mm_db_exec(HERE, db,
			    "update \"DRIVE\" set "
			    "\"DriveStateHard\" = 'unloaded', "
			    "\"DMName\" = DEFAULT, "
			    "\"DriveLibraryOccupied\" = 'f' "
			    "where \"DriveName\" = '%s';",
		    mount_info->cmi_drive) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_clear_drive_cmd_func: "
			    "db error updating DRIVE");
		}


		/* Delete any mountphysical, mountlogical, or stalehandle */
		if (mm_db_exec(HERE, db, "delete from \"MOUNTLOGICAL\" "\
			"where \"DriveName\" = '%s';",
			mount_info->cmi_drive) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_clear_drive_cmd_func: "
			    "db error deleting MOUNTLOGICAL");
		}
		if (mm_db_exec(HERE, db, "delete from \"STALEHANDLE\" "\
			"where \"DriveName\" = '%s';",
			mount_info->cmi_drive) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_clear_drive_cmd_func: "
			    "db error deleting STALEHANDLE");
		}
		if (mm_db_exec(HERE, db, "delete from \"MOUNTPHYSICAL\" "\
			"where \"DriveName\" = '%s';",
			mount_info->cmi_drive) != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_clear_drive_cmd_func: "
			    "db error deleting MOUNTPHYSICAL");
		}

		if (mm_has_depend(cmd)) {
			return (MM_DEPEND_DONE);
		}
		return (MM_CMD_DONE);
	}
	mms_trace(MMS_ERR,
	    "mm_clear_drive_cmd_func: unknown cmd state");
	return (MM_CMD_ERROR);
}

mm_command_t *
mm_add_clear_drive(char *drive_name, mm_data_t *mm_data,
		mm_db_t *db,
		mm_command_t *parent_cmd, char *cart_pcl, int force,
		int nonphysical) {

	mm_command_t		*cmd;
	mm_wka_t		*lm_wka;
	PGresult		*lm_name;
	uuid_text_t		uuid;

	mm_command_t *cur_cmd;

	mms_trace(MMS_DEVP, "Adding mm_clear_drive_cmd_func");

	if (drive_name == NULL) {
		mms_trace(MMS_DEBUG,
		    "mm_add_clear_drive passed null drive_name");
		return (NULL);
	}
	if (mm_data == NULL) {
		mms_trace(MMS_DEBUG,
		    "mm_add_clear_drive passed null mm_data");
		return (NULL);
	}
	if (db == NULL) {
		mms_trace(MMS_DEBUG,
		    "mm_add_clear_drive passed null db pointer");
		return (NULL);
	}


	if (mm_db_exec(HERE, db,
		    "select \"LMName\", \"LibraryName\" "\
		    "from \"LM\" "			    \
		    "where \"LibraryName\" in "\
		    "(select \"LibraryName\" from "\
		    "\"DRIVE\" where \"DriveName\" = '%s');",
		    drive_name) != MM_DB_DATA) {
		mms_trace(MMS_ERR, "Couldn't get LM Name -> "	\
		    "mm_add_clear_drive");
		mm_clear_db(&db->mm_db_results);
		return (NULL);
	}
	lm_name = db->mm_db_results;
	if (PQntuples(lm_name) == 0) {
		mms_trace(MMS_ERR, "Couldn't get LM Name -> "	\
		    "mm_add_clear_drive");
		mm_clear_db(&lm_name);
		return (NULL);
	}

	/* Check the queue, if there is already a clear drive, */
	/* do not add another */
	/* If other is found, return a pointer to that clear drive */
	pthread_mutex_lock(&mm_data->mm_queue_mutex);
		mms_list_foreach(&mm_data->mm_cmd_queue, cur_cmd) {
			if ((cur_cmd->cmd_func == mm_clear_drive_cmd_func) ||
			    (cur_cmd->cmd_func ==
			    mm_non_physical_clear_cmd_func)) {
				if ((strcmp(cur_cmd->cmd_mount_info.cmi_drive,
				    drive_name) == 0) &&
				    (strcmp(cur_cmd->cmd_mount_info.cmi_library,
				    PQgetvalue(lm_name, 0, 1)) == 0)) {
					mms_trace(MMS_ERR,
					    "already have a clear "
					    "drive for %s , %s",
					    drive_name,
					    PQgetvalue(lm_name, 0, 1));
					pthread_mutex_unlock(&mm_data->
					    mm_queue_mutex);
					mm_clear_db(&lm_name);
					return (cur_cmd);
				}

			}
		}
	pthread_mutex_unlock(&mm_data->mm_queue_mutex);



	lm_wka = NULL;
	mms_list_foreach(&mm_data->mm_wka_list, lm_wka) {
		if (strcmp(lm_wka->wka_conn.cci_instance,
			    PQgetvalue(lm_name, 0, 0)) == 0) {
			/* Found the wka of lm */
			break;
		}
	}
	if ((lm_wka == NULL) || (strcmp(lm_wka->wka_conn.cci_instance,
					PQgetvalue(lm_name, 0, 0)) != 0)) {
		/* bad wka */
		mms_trace(MMS_DEBUG, "Could not find a connected LM");
		mm_clear_db(&lm_name);
		return (NULL);
	}
	if ((cmd = mm_alloc_cmd(lm_wka)) == NULL) {
		mms_trace(MMS_ERR,
			"Unable to malloc mm_command_t: %s",
			strerror(errno));
		mm_clear_db(&lm_name);
		return (NULL);
	}
	if (parent_cmd != NULL) {
		mm_add_depend(cmd, parent_cmd);
	}
	mm_get_uuid(uuid);
	cmd->cmd_root = NULL;
	cmd->cmd_task = NULL;
	cmd->cmd_task = strdup(uuid);
	if (cmd->cmd_task == NULL) {
		mms_trace(MMS_ERR, "Error malloc cmd_task in add cmd");
		mm_clear_db(&lm_name);
		return (NULL);
	}
	if (nonphysical) {
		cmd->cmd_func = mm_non_physical_clear_cmd_func;
	} else {
		cmd->cmd_func = mm_clear_drive_cmd_func;
	}
	cmd->cmd_name = strdup("clear drive");

	cmd->cmd_mount_info.cmi_drive = strdup(drive_name);
	cmd->cmd_mount_info.cmi_library = strdup(PQgetvalue(lm_name, 0, 1));

	/* Force or not */
	cmd->cmd_mount_info.cui_force = force;

	/* Cart_pcl */
	if (cart_pcl != NULL) {
		mm_set_mount_info_pcl(cart_pcl,
		    &cmd->cmd_mount_info);
	}
	pthread_mutex_lock(&mm_data->mm_queue_mutex);
	mms_list_insert_tail(&mm_data->mm_cmd_queue, cmd);
	pthread_mutex_unlock(&mm_data->mm_queue_mutex);
	mm_clear_db(&lm_name);
	return (cmd);

}