Пример #1
0
/*
 * Main entry point to this module.
 *
 * Process the data from *res according to the options in pset (global),
 * to generate the horizontal and vertical headers contents,
 * then call printCrosstab() for the actual output.
 */
bool
PrintResultsInCrosstab(const PGresult *res)
{
	bool		retval = false;
	avl_tree	piv_columns;
	avl_tree	piv_rows;
	pivot_field *array_columns = NULL;
	pivot_field *array_rows = NULL;
	int			num_columns = 0;
	int			num_rows = 0;
	int			field_for_rows;
	int			field_for_columns;
	int			field_for_data;
	int			sort_field_for_columns;
	int			rn;

	avlInit(&piv_rows);
	avlInit(&piv_columns);

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		psql_error(_("\\crosstabview: query must return results to be shown in crosstab\n"));
		goto error_return;
	}

	if (PQnfields(res) < 3)
	{
		psql_error(_("\\crosstabview: query must return at least three columns\n"));
		goto error_return;
	}

	/* Process first optional arg (vertical header column) */
	if (pset.ctv_args[0] == NULL)
		field_for_rows = 0;
	else
	{
		field_for_rows = indexOfColumn(pset.ctv_args[0], res);
		if (field_for_rows < 0)
			goto error_return;
	}

	/* Process second optional arg (horizontal header column) */
	if (pset.ctv_args[1] == NULL)
		field_for_columns = 1;
	else
	{
		field_for_columns = indexOfColumn(pset.ctv_args[1], res);
		if (field_for_columns < 0)
			goto error_return;
	}

	/* Insist that header columns be distinct */
	if (field_for_columns == field_for_rows)
	{
		psql_error(_("\\crosstabview: vertical and horizontal headers must be different columns\n"));
		goto error_return;
	}

	/* Process third optional arg (data column) */
	if (pset.ctv_args[2] == NULL)
	{
		int			i;

		/*
		 * If the data column was not specified, we search for the one not
		 * used as either vertical or horizontal headers.  Must be exactly
		 * three columns, or this won't be unique.
		 */
		if (PQnfields(res) != 3)
		{
			psql_error(_("\\crosstabview: data column must be specified when query returns more than three columns\n"));
			goto error_return;
		}

		field_for_data = -1;
		for (i = 0; i < PQnfields(res); i++)
		{
			if (i != field_for_rows && i != field_for_columns)
			{
				field_for_data = i;
				break;
			}
		}
		Assert(field_for_data >= 0);
	}
	else
	{
		field_for_data = indexOfColumn(pset.ctv_args[2], res);
		if (field_for_data < 0)
			goto error_return;
	}

	/* Process fourth optional arg (horizontal header sort column) */
	if (pset.ctv_args[3] == NULL)
		sort_field_for_columns = -1;	/* no sort column */
	else
	{
		sort_field_for_columns = indexOfColumn(pset.ctv_args[3], res);
		if (sort_field_for_columns < 0)
			goto error_return;
	}

	/*
	 * First part: accumulate the names that go into the vertical and
	 * horizontal headers, each into an AVL binary tree to build the set of
	 * DISTINCT values.
	 */

	for (rn = 0; rn < PQntuples(res); rn++)
	{
		char	   *val;
		char	   *val1;

		/* horizontal */
		val = PQgetisnull(res, rn, field_for_columns) ? NULL :
			PQgetvalue(res, rn, field_for_columns);
		val1 = NULL;

		if (sort_field_for_columns >= 0 &&
			!PQgetisnull(res, rn, sort_field_for_columns))
			val1 = PQgetvalue(res, rn, sort_field_for_columns);

		avlMergeValue(&piv_columns, val, val1);

		if (piv_columns.count > CROSSTABVIEW_MAX_COLUMNS)
		{
			psql_error(_("\\crosstabview: maximum number of columns (%d) exceeded\n"),
					   CROSSTABVIEW_MAX_COLUMNS);
			goto error_return;
		}

		/* vertical */
		val = PQgetisnull(res, rn, field_for_rows) ? NULL :
			PQgetvalue(res, rn, field_for_rows);

		avlMergeValue(&piv_rows, val, NULL);
	}

	/*
	 * Second part: Generate sorted arrays from the AVL trees.
	 */

	num_columns = piv_columns.count;
	num_rows = piv_rows.count;

	array_columns = (pivot_field *)
		pg_malloc(sizeof(pivot_field) * num_columns);

	array_rows = (pivot_field *)
		pg_malloc(sizeof(pivot_field) * num_rows);

	avlCollectFields(&piv_columns, piv_columns.root, array_columns, 0);
	avlCollectFields(&piv_rows, piv_rows.root, array_rows, 0);

	/*
	 * Third part: optionally, process the ranking data for the horizontal
	 * header
	 */
	if (sort_field_for_columns >= 0)
		rankSort(num_columns, array_columns);

	/*
	 * Fourth part: print the crosstab'ed results.
	 */
	retval = printCrosstab(res,
						   num_columns, array_columns, field_for_columns,
						   num_rows, array_rows, field_for_rows,
						   field_for_data);

error_return:
	avlFree(&piv_columns, piv_columns.root);
	avlFree(&piv_rows, piv_rows.root);
	pg_free(array_columns);
	pg_free(array_rows);

	return retval;
}
Пример #2
0
/*
 * Parse "arg", which is a string of column IDs separated by "separator".
 *
 * Each column ID can be:
 * - a number from 1 to PQnfields(res)
 * - an unquoted column name matching (case insensitively) one of PQfname(res,...)
 * - a quoted column name matching (case sensitively) one of PQfname(res,...)
 *
 * If max_columns > 0, it is the max number of column IDs allowed.
 *
 * On success, return number of column IDs found (possibly 0), and return a
 * malloc'd array of the matching column numbers of "res" into *col_numbers.
 *
 * On failure, return -1 and set *col_numbers to NULL.
 */
static int
parseColumnRefs(const char *arg,
				const PGresult *res,
				int **col_numbers,
				int max_columns,
				char separator)
{
	const char *p = arg;
	char		c;
	int			num_cols = 0;

	*col_numbers = NULL;
	while ((c = *p) != '\0')
	{
		const char *field_start = p;
		bool		quoted_field = false;

		/* first char */
		if (c == '"')
		{
			quoted_field = true;
			p++;
		}

		while ((c = *p) != '\0')
		{
			if (c == separator && !quoted_field)
				break;
			if (c == '"')		/* end of field or embedded double quote */
			{
				p++;
				if (*p == '"')
				{
					if (quoted_field)
					{
						p++;
						continue;
					}
				}
				else if (quoted_field && *p == separator)
					break;
			}
			if (*p)
				p += PQmblen(p, pset.encoding);
		}

		if (p != field_start)
		{
			char   *col_name;
			int		col_num;

			/* enforce max_columns limit */
			if (max_columns > 0 && num_cols == max_columns)
			{
				psql_error(_("No more than %d column references expected\n"),
						   max_columns);
				goto errfail;
			}
			/* look up the column and add its index into *col_numbers */
			col_name = pg_malloc(p - field_start + 1);
			memcpy(col_name, field_start, p - field_start);
			col_name[p - field_start] = '\0';
			col_num = indexOfColumn(col_name, res);
			pg_free(col_name);
			if (col_num < 0)
				goto errfail;
			*col_numbers = (int *) pg_realloc(*col_numbers,
											  (num_cols + 1) * sizeof(int));
			(*col_numbers)[num_cols++] = col_num;
		}
		else
		{
			psql_error(_("Empty column reference\n"));
			goto errfail;
		}

		if (*p)
			p += PQmblen(p, pset.encoding);
	}
	return num_cols;

errfail:
	pg_free(*col_numbers);
	*col_numbers = NULL;
	return -1;
}