Exemplo n.º 1
0
/*
 * create and populate the crosstab tuplestore using the provided source query
 */
static Tuplestorestate *
get_crosstab_tuplestore(char *sql,
						HTAB *crosstab_hash,
						TupleDesc tupdesc,
						MemoryContext per_query_ctx,
						bool randomAccess)
{
	Tuplestorestate *tupstore;
	int			num_categories = hash_get_num_entries(crosstab_hash);
	AttInMetadata *attinmeta = TupleDescGetAttInMetadata(tupdesc);
	char	  **values;
	HeapTuple	tuple;
	int			ret;
	int			proc;

	/* initialize our tuplestore (while still in query context!) */
	tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);

	/* Connect to SPI manager */
	if ((ret = SPI_connect()) < 0)
		/* internal error */
		elog(ERROR, "get_crosstab_tuplestore: SPI_connect returned %d", ret);

	/* Now retrieve the crosstab source rows */
	ret = SPI_execute(sql, true, 0);
	proc = SPI_processed;

	/* Check for qualifying tuples */
	if ((ret == SPI_OK_SELECT) && (proc > 0))
	{
		SPITupleTable *spi_tuptable = SPI_tuptable;
		TupleDesc	spi_tupdesc = spi_tuptable->tupdesc;
		int			ncols = spi_tupdesc->natts;
		char	   *rowid;
		char	   *lastrowid = NULL;
		bool		firstpass = true;
		int			i,
					j;
		int			result_ncols;

		if (num_categories == 0)
		{
			/* no qualifying category tuples */
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("provided \"categories\" SQL must " \
							"return 1 column of at least one row")));
		}

		/*
		 * The provided SQL query must always return at least three columns:
		 *
		 * 1. rowname	the label for each row - column 1 in the final result
		 * 2. category	the label for each value-column in the final result 3.
		 * value	 the values used to populate the value-columns
		 *
		 * If there are more than three columns, the last two are taken as
		 * "category" and "values". The first column is taken as "rowname".
		 * Additional columns (2 thru N-2) are assumed the same for the same
		 * "rowname", and are copied into the result tuple from the first time
		 * we encounter a particular rowname.
		 */
		if (ncols < 3)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid source data SQL statement"),
					 errdetail("The provided SQL must return 3 " \
							   " columns; rowid, category, and values.")));

		result_ncols = (ncols - 2) + num_categories;

		/* Recheck to make sure we tuple descriptor still looks reasonable */
		if (tupdesc->natts != result_ncols)
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("invalid return type"),
					 errdetail("Query-specified return " \
							   "tuple has %d columns but crosstab " \
							   "returns %d.", tupdesc->natts, result_ncols)));

		/* allocate space */
		values = (char **) palloc(result_ncols * sizeof(char *));

		/* and make sure it's clear */
		memset(values, '\0', result_ncols * sizeof(char *));

		for (i = 0; i < proc; i++)
		{
			HeapTuple	spi_tuple;
			crosstab_cat_desc *catdesc;
			char	   *catname;

			/* get the next sql result tuple */
			spi_tuple = spi_tuptable->vals[i];

			/* get the rowid from the current sql result tuple */
			rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);

			/*
			 * if we're on a new output row, grab the column values up to
			 * column N-2 now
			 */
			if (firstpass || !xstreq(lastrowid, rowid))
			{
				/*
				 * a new row means we need to flush the old one first, unless
				 * we're on the very first row
				 */
				if (!firstpass)
				{
					/* rowid changed, flush the previous output row */
					tuple = BuildTupleFromCStrings(attinmeta, values);

					tuplestore_puttuple(tupstore, tuple);

					for (j = 0; j < result_ncols; j++)
						xpfree(values[j]);
				}

				values[0] = rowid;
				for (j = 1; j < ncols - 2; j++)
					values[j] = SPI_getvalue(spi_tuple, spi_tupdesc, j + 1);

				/* we're no longer on the first pass */
				firstpass = false;
			}

			/* look up the category and fill in the appropriate column */
			catname = SPI_getvalue(spi_tuple, spi_tupdesc, ncols - 1);

			if (catname != NULL)
			{
				crosstab_HashTableLookup(crosstab_hash, catname, catdesc);

				if (catdesc)
					values[catdesc->attidx + ncols - 2] =
						SPI_getvalue(spi_tuple, spi_tupdesc, ncols);
			}

			xpfree(lastrowid);
			xpstrdup(lastrowid, rowid);
		}

		/* flush the last output row */
		tuple = BuildTupleFromCStrings(attinmeta, values);

		tuplestore_puttuple(tupstore, tuple);
	}

	if (SPI_finish() != SPI_OK_FINISH)
		/* internal error */
		elog(ERROR, "get_crosstab_tuplestore: SPI_finish() failed");

	tuplestore_donestoring(tupstore);

	return tupstore;
}
Exemplo n.º 2
0
Datum
crosstab(PG_FUNCTION_ARGS)
{
	char	   *sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	Tuplestorestate *tupstore;
	TupleDesc	tupdesc;
	int			call_cntr;
	int			max_calls;
	AttInMetadata *attinmeta;
	SPITupleTable *spi_tuptable;
	TupleDesc	spi_tupdesc;
	bool		firstpass;
	char	   *lastrowid;
	int			i;
	int			num_categories;
	MemoryContext per_query_ctx;
	MemoryContext oldcontext;
	int			ret;
	int			proc;

	/* check to see if caller supports us returning a tuplestore */
	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set")));
	if (!(rsinfo->allowedModes & SFRM_Materialize))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("materialize mode required, but it is not " \
						"allowed in this context")));

	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;

	/* Connect to SPI manager */
	if ((ret = SPI_connect()) < 0)
		/* internal error */
		elog(ERROR, "crosstab: SPI_connect returned %d", ret);

	/* Retrieve the desired rows */
	ret = SPI_execute(sql, true, 0);
	proc = SPI_processed;

	/* If no qualifying tuples, fall out early */
	if (ret != SPI_OK_SELECT || proc <= 0)
	{
		SPI_finish();
		rsinfo->isDone = ExprEndResult;
		PG_RETURN_NULL();
	}

	spi_tuptable = SPI_tuptable;
	spi_tupdesc = spi_tuptable->tupdesc;

	/*----------
	 * The provided SQL query must always return three columns.
	 *
	 * 1. rowname
	 *	the label or identifier for each row in the final result
	 * 2. category
	 *	the label or identifier for each column in the final result
	 * 3. values
	 *	the value for each column in the final result
	 *----------
	 */
	if (spi_tupdesc->natts != 3)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid source data SQL statement"),
				 errdetail("The provided SQL must return 3 "
						   "columns: rowid, category, and values.")));

	/* get a tuple descriptor for our result type */
	switch (get_call_result_type(fcinfo, NULL, &tupdesc))
	{
		case TYPEFUNC_COMPOSITE:
			/* success */
			break;
		case TYPEFUNC_RECORD:
			/* failed to determine actual type of RECORD */
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("function returning record called in context "
							"that cannot accept type record")));
			break;
		default:
			/* result type isn't composite */
			elog(ERROR, "return type must be a row type");
			break;
	}

	/*
	 * Check that return tupdesc is compatible with the data we got from SPI,
	 * at least based on number and type of attributes
	 */
	if (!compatCrosstabTupleDescs(tupdesc, spi_tupdesc))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("return and sql tuple descriptions are " \
						"incompatible")));

	/*
	 * switch to long-lived memory context
	 */
	oldcontext = MemoryContextSwitchTo(per_query_ctx);

	/* make sure we have a persistent copy of the result tupdesc */
	tupdesc = CreateTupleDescCopy(tupdesc);

	/* initialize our tuplestore in long-lived context */
	tupstore =
		tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
							  false, work_mem);

	MemoryContextSwitchTo(oldcontext);

	/*
	 * Generate attribute metadata needed later to produce tuples from raw C
	 * strings
	 */
	attinmeta = TupleDescGetAttInMetadata(tupdesc);

	/* total number of tuples to be examined */
	max_calls = proc;

	/* the return tuple always must have 1 rowid + num_categories columns */
	num_categories = tupdesc->natts - 1;

	firstpass = true;
	lastrowid = NULL;

	for (call_cntr = 0; call_cntr < max_calls; call_cntr++)
	{
		bool		skip_tuple = false;
		char	  **values;

		/* allocate and zero space */
		values = (char **) palloc0((1 + num_categories) * sizeof(char *));

		/*
		 * now loop through the sql results and assign each value in sequence
		 * to the next category
		 */
		for (i = 0; i < num_categories; i++)
		{
			HeapTuple	spi_tuple;
			char	   *rowid;

			/* see if we've gone too far already */
			if (call_cntr >= max_calls)
				break;

			/* get the next sql result tuple */
			spi_tuple = spi_tuptable->vals[call_cntr];

			/* get the rowid from the current sql result tuple */
			rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);

			/*
			 * If this is the first pass through the values for this rowid,
			 * set the first column to rowid
			 */
			if (i == 0)
			{
				xpstrdup(values[0], rowid);

				/*
				 * Check to see if the rowid is the same as that of the last
				 * tuple sent -- if so, skip this tuple entirely
				 */
				if (!firstpass && xstreq(lastrowid, rowid))
				{
					xpfree(rowid);
					skip_tuple = true;
					break;
				}
			}

			/*
			 * If rowid hasn't changed on us, continue building the output
			 * tuple.
			 */
			if (xstreq(rowid, values[0]))
			{
				/*
				 * Get the next category item value, which is always attribute
				 * number three.
				 *
				 * Be careful to assign the value to the array index based on
				 * which category we are presently processing.
				 */
				values[1 + i] = SPI_getvalue(spi_tuple, spi_tupdesc, 3);

				/*
				 * increment the counter since we consume a row for each
				 * category, but not for last pass because the outer loop will
				 * do that for us
				 */
				if (i < (num_categories - 1))
					call_cntr++;
				xpfree(rowid);
			}
			else
			{
				/*
				 * We'll fill in NULLs for the missing values, but we need to
				 * decrement the counter since this sql result row doesn't
				 * belong to the current output tuple.
				 */
				call_cntr--;
				xpfree(rowid);
				break;
			}
		}

		if (!skip_tuple)
		{
			HeapTuple	tuple;

			/* build the tuple and store it */
			tuple = BuildTupleFromCStrings(attinmeta, values);
			tuplestore_puttuple(tupstore, tuple);
			heap_freetuple(tuple);
		}

		/* Remember current rowid */
		xpfree(lastrowid);
		xpstrdup(lastrowid, values[0]);
		firstpass = false;

		/* Clean up */
		for (i = 0; i < num_categories + 1; i++)
			if (values[i] != NULL)
				pfree(values[i]);
		pfree(values);
	}

	/* let the caller know we're sending back a tuplestore */
	rsinfo->returnMode = SFRM_Materialize;
	rsinfo->setResult = tupstore;
	rsinfo->setDesc = tupdesc;

	/* release SPI related resources (and return to caller's context) */
	SPI_finish();

	return (Datum) 0;
}
Exemplo n.º 3
0
/********************************************************************
 *
 * fork/exec a child pdm after setting up a message pipe. 
 */
void mgr_launch_pdm( XpPdmServiceRec *rec )
{
    int       i;
    struct sigaction svec;
    char      buf[1024];
    int       original_umask;
    char      *existing_name;
    FILE      *existing_file;
    Xauth     *entry;
    char      *envstr;


    /*
     * Setup message pipe.
     */
    if ( pipe(rec->message_pipe) == -1 ) {
	rec->pdm_exec_errorcode = g.pdm_start_error;
	sprintf( buf, PDMD_MSG_8, g.prog_name );
	rec->pdm_exec_errormessage = xpstrdup( buf );
	return;
    }

    rec->message_xtid = XtAppAddInput( g.context, rec->message_pipe[0],
			  (XtPointer) XtInputReadMask,
			  message_pipe_handler, (XtPointer) NULL );

    /*
     * See if a cookie file is needed.
     */
    if (rec->cookie_cnt) {
	/*
	 * Create new .Xauthority file.
	 */
	original_umask = umask (0077);      /* disallow non-owner access */
	tmpnam( rec->auth_filename );
	rec->auth_file = fopen( rec->auth_filename, "w" );

	if (rec->auth_file) {
	    /*
	     * Copy existing .Xauthority entries.
	     */
	    existing_name = XauFileName ();

	    if (existing_name) {
		if (access (existing_name, R_OK) == 0) {     /* checks REAL id */
		    existing_file = fopen (existing_name, "r");
		    if (existing_file) {
			for (;;) {
			    entry = XauReadAuth (existing_file);
			    if (!entry)
				break;

			    XauWriteAuth( rec->auth_file, entry );
			    XauDisposeAuth (entry);
			}
			fclose (existing_file);
		    }
		}
	    }

	    /*
	     * Merge in cookies recently sent.
	     */
	    for ( i = 0; i < rec->cookie_cnt; i++ ) {
		XauWriteAuth( rec->auth_file, rec->cookies[i] );
	    }

	    fclose( rec->auth_file );
	}
	original_umask = umask (original_umask);
    }


    rec->pid = fork();

    if ( rec->pid < 0 ) {
	rec->pdm_exec_errorcode = g.pdm_start_error;
	sprintf( buf, PDMD_MSG_9, g.prog_name );
	rec->pdm_exec_errormessage = xpstrdup( buf );
	return;
    }
    else if ( rec->pid == 0) {
	/*
	 * Child process.
	 */

	/*
	 * Hook stderr back to parent via message pipe.
	 */
	dup2(rec->message_pipe[1], 2);
	close(rec->message_pipe[0]);

	/*
	 * The child should have default behavior for all signals.
	 */
	sigemptyset(&svec.sa_mask);
	svec.sa_flags   = 0;
	svec.sa_handler = SIG_DFL;
	(void) sigaction(SIGCHLD, &svec, (struct sigaction *) NULL);

	for (i=3; i < FOPEN_MAX; i++) {
	    if ((i != rec->message_pipe[1]) && 
		(rec->auth_file && (i != fileno(rec->auth_file))))
	    {
		(void) fcntl (i, F_SETFD, 1);
	    }
	}

	/*
	 * Set the new locale for the child.
	 *
	 * note: the locale hint will be of the form:
	 *
	 *    name_spec[;registry_spec[;ver_spec[;encoding_spec]]]
	 *
	 * for now, just pull out the name_spec (e.g. 'C')
	 * and use it.   With a little work, a more complex
	 * syntax could be understood and the appropriate
	 * actions taken here rather than just wedging
	 * name_spec into setlocale() and hoping.
	 */
	if ( !(rec->locale_hint) ) {
	    /*
	     * Leave current locale alone.
	     */
	}
	else if ( strcmp( rec->locale_hint, "" ) ) {
	    /*
	     * Leave current locale alone.  Note that "" into
	     * setlocale says to go with default vs leave it alone.
	     */
	}
	else {
	    char *tptr1, *tptr2;

	    tptr1 = xpstrdup( rec->locale_hint );
            tptr2 = strchr( tptr1, ';' );
	    if (tptr2) *tptr2 = '\0';
	
	    setlocale( LC_ALL, tptr1 );
	    XFree( tptr1 );
	}

	/*
	 * Set XAUTHORITY env var if needed.
	 */
	if ((rec->cookie_cnt) && (rec->auth_filename) && (rec->auth_file)) {
	    envstr = Xmalloc( strlen(rec->auth_filename) + 12 );
	    sprintf( envstr, "XAUTHORITY=%s", rec->auth_filename );
	    putenv( envstr );
	}

	/*
	 * Start the child for real.
	 */
	(void) execvp(rec->pdm_exec_argvs[0], rec->pdm_exec_argvs);

	(void) fprintf (stderr, PDMD_MSG_10, g.prog_name, rec->pdm_exec_argvs[0]);

	/*
	 * tomg - need to deal with failed child start.
	 */
	exit(PDM_EXIT_ERROR);
    }
    else {
	/*
	 * Parent process.
	 */

	/*
	 * Close the write end of the pipe - only the child needs it.
	 */
	close(rec->message_pipe[1]);
	rec->message_pipe[1] = -1;
    }
}
Exemplo n.º 4
0
/********************************************************************
 *
 * Figure out which pdm executable to later fork/exec.
 */
void mgr_fetch_pdm( XpPdmServiceRec *rec )
{
    char tstr[1024], *tptr1, *tptr2, *tptr3;
    int  firstTime;
    long now;
    Display *tdpy;
    int lxerrno;

    if ( g.override_pdm ) {
	/*
	 * Override all defaults and other possible settings.
	 */
	tptr1 = xpstrdup(g.override_pdm);
    }
    else {
	/*
	 * See if the print context says which pdm to run.
	 */
	g.xerrno = 0;		/* Error Handler */
	lxerrno = 0;		/* XIO Error Handler */

	if ( setjmp( xio_quickie_jmp_buf ) == 0 ) {
	    XSetIOErrorHandler( xio_quickie_handler );

#if 0 && defined(PRINTING_SUPPORTED)
	    if ( rec->seldpy_as_printdpy ) {
		tptr1 = XpGetOneAttribute( rec->selection_display,
				   rec->print_context,
				   XPPrinterAttr, "dt-pdm-command" );
	    }
	    else {
		tdpy = XOpenDisplay( rec->print_display_str );
		if (tdpy) {
		    tptr1 = XpGetOneAttribute( tdpy,
                                   rec->print_context,
                                   XPPrinterAttr, "dt-pdm-command" );
		    XCloseDisplay( tdpy );
		}
	    }
#endif /* PRINTING_SUPPORTED */

	    XSetIOErrorHandler( NULL );
	}
	else {
	    lxerrno = 1;

	    XSetIOErrorHandler( NULL );
	}

	/*
	 * See if we got a useful pdm exec string.  Use
	 * default if not.
	 */
	if ( g.xerrno || lxerrno ) {
	    rec->pdm_exec_errorcode = g.pdm_start_error;
	    return;
	}
	else if (!tptr1) {
	    tptr1 = xpstrdup(g.default_pdm);
	}
	else if (!tptr1[0]) {
	    tptr1 = xpstrdup(g.default_pdm);
	}
    }
    
    /*
     * Convert pdm-command into argv[] style array.
     *
     * Note: this parsing code does NOT respect shell
     * quotes and other items.   --tomg
     */
    rec->pdm_exec_argvs = (char **) NULL;

    tptr2 = tptr1;	/* retain orig pointer for freeing */
    firstTime = 1;

    while (1) {
	if (firstTime) {
	    tptr3 = xpstrtok( tptr2, " \n\t" );
	    firstTime = 0;

	    if (!tptr3) {
		/*
		 * There were NO useful tokens to begin with, so
		 * we'll have to fall back on the default.
		 */
		xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup( g.default_pdm ));
		break;
	    }
	}
	else {
	    tptr3 = xpstrtok( (char *) NULL, " \n\t" );
	}

	if (tptr3) {
	    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup( tptr3 ) );
	}
	else {
	    break;
	}
    }
    Xfree(tptr1);

    /*
     * Add standard command line parameters.
     */
    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-display") );
    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(rec->video_display_str) );

    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-window") );
    sprintf( tstr, "0x%lx", rec->video_window );
    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(tstr) );

    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-pdisplay") );
    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(rec->print_display_str) );

    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-pwindow") );
    sprintf( tstr, "0x%lx", rec->print_window );
    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(tstr) );

#if 0 && defined(PRINTING_SUPPORTED)
    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup("-pcontext") );
    sprintf( tstr, "0x%lx", rec->print_context );
    xp_add_argv( &(rec->pdm_exec_argvs), xpstrdup(tstr) );
#endif /* PRINTING_SUPPORTED */
}
Exemplo n.º 5
0
/********************************************************************
 *
 * Setup a child service record per the selection request.
 */
void mgr_initialize( XEvent *report, XpPdmServiceRec *rec )
{
    Display *testdpy;
    char    buf[1024];

    Display *selection_display;
    Window   requestor;
    Atom     prop_atom;
    unsigned long tafter;

    XTextProperty  text_prop;
    char           **list;
    int            list_cnt;

    /*
     * Grab the PDM_CLIENT_PROP from which all the juicy
     * information is retrieved.
     */
    selection_display = report->xselectionrequest.display;
    requestor = report->xselectionrequest.requestor;
    prop_atom = report->xselectionrequest.property;

    if ( XGetWindowProperty( selection_display, requestor, prop_atom,
			0, 100000, True, AnyPropertyType,
                        &text_prop.encoding,
			&text_prop.format,
			&text_prop.nitems,
			&tafter,
                        &text_prop.value ) != Success ) {
	/*
	 * Error
	 */
	rec->pdm_exec_errorcode = g.pdm_start_error;

	sprintf( buf, PDMD_MSG_5, g.prog_name );
	rec->pdm_exec_errormessage = xpstrdup( buf );

	return;
    }

    if ( text_prop.format != 8 ) {
	/*
	 * Error
	 */
	rec->pdm_exec_errorcode = g.pdm_start_error;

	sprintf( buf, PDMD_MSG_6, g.prog_name );
	rec->pdm_exec_errormessage = xpstrdup( buf );

	return;
    }

    if ( XmbTextPropertyToTextList( selection_display, &text_prop,
				     &list, &list_cnt ) < 0 ) {
	/*
	 * Error
	 */
	rec->pdm_exec_errorcode = g.pdm_start_error;

	sprintf( buf, PDMD_MSG_7, g.prog_name );
	rec->pdm_exec_errormessage = xpstrdup( buf );

	return;
    }

    /*
     * Fill in the PDM_MANAGER portion of the client record.
     */
    rec->video_display_str  = xpstrdup( list[0] );
    rec->video_window       = strtol(list[1], (char **)NULL, 16);
    rec->print_display_str  = xpstrdup( list[2] );
    rec->print_window       = strtol(list[3], (char **)NULL, 16);
#if 0 && defined(PRINTING_SUPPORTED)
    rec->print_context      = strtol(list[4], (char **)NULL, 16);
#endif /* PRINTING_SUPPORTED */
    rec->locale_hint        = xpstrdup( list[5] );
    XFreeStringList( list );

    rec->selection_display  = selection_display;
    rec->requestor          = requestor;
    rec->prop_atom          = prop_atom;
    rec->selection          = report->xselectionrequest.selection;
    rec->time               = report->xselectionrequest.time;

    rec->mgr_flag           = True;	/* mgr portion of rec now valid */

    /*
     * Optimization.  The only live display connection, for which we
     * need to trap XIO errors, is "selection display".  For the
     * "video" and "print" displays, we have the display strings and
     * can establish connections as we need them.  Since they are rarely
     * used, and opening them up here would create XIO liability problems
     * and a startup performance hit, we won't establish connections now.
     *
     * One optimization however is to see if the "print" display would
     * just happen to be the same at the "selection display" currently
     * open.
     */
    if ( !strcmp( XDisplayString(rec->selection_display),
		  rec->print_display_str ) ) {
	rec->seldpy_as_printdpy = True;
    }
    else {
	rec->seldpy_as_printdpy = False;

#ifdef OPTIONAL_PXAUTH_PRETEST
	/*
	 * Verify connectability to the Print Server.
	 *
	 * Note: once beyond the selection phase, all communication
	 *       will be by way of the Print Server.  If we cannot
	 *       connect later, then we will have no way to deliver
	 *       EXIT_PXAUTH, EXIT_VXAUTH, EXIT_ERROR, EXIT_OK or
         *       EXIT_CANCEL.  Real bad news!
	 *
	 * It is better to discover now that we don't have
	 * connection authorization for the print display since
	 * we can still let the user know of PXAUTH problems
	 * via the selection display currently open.
	 *
	 * Unfortunately, this pre-test is a performance hit in the
	 * startup phase.
	 */
	if ( ! (testdpy = XOpenDisplay(rec->print_display_str)) ) {
	    rec->pdm_exec_errorcode = g.pdm_start_pxauth;
	    return;
	}
	XCloseDisplay( testdpy );
#endif /* OPTIONAL_PXAUTH_PRETEST */
    }

#ifdef OPTIONAL_VXAUTH_PRETEST
    /*
     * Verify connectability to the Video Server.
     *
     * It is better to discover now that we don't have
     * connection authorization for the video display since
     * we can still let the user know of VXAUTH problems
     * via the selection display currently open.
     *
     * Unfortunately, this pre-test is a performance hit in the
     * startup phase.
     */
    if ( ! (testdpy = XOpenDisplay(rec->video_display_str)) ) {
	rec->pdm_exec_errorcode = g.pdm_start_vxauth;
	return;
    }
    XCloseDisplay( testdpy );
#endif /* OPTIONAL_VXAUTH_PRETEST */

}