コード例 #1
0
ファイル: extension.c プロジェクト: eulerto/pgquarrel
PQLExtension *
getExtensions(PGconn *c, int *n)
{
	PQLExtension	*e;
	PGresult		*res;
	int				i;

	logNoise("extension: server version: %d", PQserverVersion(c));

	/* bail out if we do not support it */
	if (PQserverVersion(c) < 90100)
	{
		logWarning("ignoring extensions because server does not support it");
		return NULL;
	}

	res = PQexec(c,
				 "SELECT e.oid, extname AS extensionname, nspname, extversion AS version, extrelocatable, obj_description(e.oid, 'pg_extension') AS description FROM pg_extension e LEFT JOIN pg_namespace n ON (e.extnamespace = n.oid) ORDER BY extname");

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	*n = PQntuples(res);
	if (*n > 0)
		e = (PQLExtension *) malloc(*n * sizeof(PQLExtension));
	else
		e = NULL;

	logDebug("number of extensions in server: %d", *n);

	for (i = 0; i < *n; i++)
	{
		e[i].oid = strtoul(PQgetvalue(res, i, PQfnumber(res, "oid")), NULL, 10);
		e[i].extensionname = strdup(PQgetvalue(res, i, PQfnumber(res,
											   "extensionname")));
		e[i].schemaname = strdup(PQgetvalue(res, i, PQfnumber(res, "nspname")));
		e[i].version = strdup(PQgetvalue(res, i, PQfnumber(res, "version")));
		e[i].relocatable = (PQgetvalue(res, i, PQfnumber(res,
									   "extrelocatable"))[0] == 't');
		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			e[i].comment = NULL;
		else
			e[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));

		logDebug("extension \"%s\"", e[i].extensionname);
	}

	PQclear(res);

	return e;
}
コード例 #2
0
ファイル: sequence.c プロジェクト: fabriziomello/pgquarrel
PQLSequence *
getSequences(PGconn *c, int *n)
{
	PQLSequence		*s;
	PGresult		*res;
	int				i;

	logNoise("sequence: server version: %d", PQserverVersion(c));

	res = PQexec(c,
				 "SELECT c.oid, n.nspname, c.relname, obj_description(c.oid, 'pg_class') AS description, pg_get_userbyid(c.relowner) AS relowner, relacl FROM pg_class c INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE relkind = 'S' AND nspname !~ '^pg_' AND nspname <> 'information_schema' ORDER BY nspname, relname");

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	*n = PQntuples(res);
	if (*n > 0)
		s = (PQLSequence *) malloc(*n * sizeof(PQLSequence));
	else
		s = NULL;

	logDebug("number of sequences in server: %d", *n);

	for (i = 0; i < *n; i++)
	{
		s[i].obj.oid = strtoul(PQgetvalue(res, i, PQfnumber(res, "oid")), NULL, 10);
		s[i].obj.schemaname = strdup(PQgetvalue(res, i, PQfnumber(res, "nspname")));
		s[i].obj.objectname = strdup(PQgetvalue(res, i, PQfnumber(res, "relname")));
		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			s[i].comment = NULL;
		else
			s[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));
		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			s[i].comment = NULL;
		else
			s[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));

		s[i].owner = strdup(PQgetvalue(res, i, PQfnumber(res, "relowner")));
		if (PQgetisnull(res, i, PQfnumber(res, "relacl")))
			s[i].acl = NULL;
		else
			s[i].acl = strdup(PQgetvalue(res, i, PQfnumber(res, "relacl")));

		logDebug("sequence %s.%s", formatObjectIdentifier(s[i].obj.schemaname),
				 formatObjectIdentifier(s[i].obj.objectname));
	}

	PQclear(res);

	return s;
}
コード例 #3
0
ファイル: common.c プロジェクト: eulerto/pgquarrel
/*
 * Return a linked list that contains elements according to specified set
 * operation (setop). If there aren't options, return NULL.
 */
stringList *
setOperationOptions(char *a, char *b, int setop, bool withvalue, bool changed)
{
	stringList		*first, *second;
	stringList		*ret = NULL;
	stringListCell	*headitem = NULL;

	logNoise("options: set operation %d", setop);

	/* if a is NULL, there is neither intersection nor complement (except) */
	if (a == NULL)
		return NULL;

	/* if b is NULL, there isn't intersection */
	if (setop == PGQ_INTERSECT && b == NULL)
		return NULL;

	first = buildStringList(a);
	second = buildStringList(b);

	if (setop == PGQ_INTERSECT)
		headitem = intersectWithSortedLists(first->head, second->head, withvalue, changed);
	else if (setop == PGQ_SETDIFFERENCE)
		headitem = setDifferenceWithSortedLists(first->head, second->head, withvalue);
	else
		logError("set operation not supported");

	/* build a linked list */
	if (headitem)
	{
		stringListCell	*p;

		ret = (stringList *) malloc(sizeof(stringList));

		/* linked list head */
		ret->head = headitem;

		/* find linked list tail */
		p = headitem;
		while (p->next)
			p = p->next;
		ret->tail = p;
	}

	/* free temporary lists */
	if (first)
		freeStringList(first);
	if (second)
		freeStringList(second);

	return ret;
}
コード例 #4
0
ファイル: sequence.c プロジェクト: fabriziomello/pgquarrel
void
getSequenceAttributes(PGconn *c, PQLSequence *s)
{
	char		*query = NULL;
	int			nquery = PGQQRYLEN;
	PGresult	*res;
	int			r;

	do {
		query = (char *) malloc(nquery * sizeof(char));

		r = snprintf(query, nquery,
			"SELECT increment_by, start_value, max_value, min_value, cache_value, is_cycled FROM %s.%s",
			s->obj.schemaname, s->obj.objectname);

		if (r < nquery)
			break;

		logNoise("query size: required (%u) ; initial (%u)", r, nquery);
		nquery = r + 1;	/* make enough room for query */
		free(query);
	} while (true);

	res = PQexec(c, query);

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	if (PQntuples(res) != 1)
		logError("query to get sequence information returns %d row(s) (expected 1)",
				 PQntuples(res));
	else
	{
		s->incvalue = strdup(PQgetvalue(res, 0, PQfnumber(res, "increment_by")));
		s->startvalue = strdup(PQgetvalue(res, 0, PQfnumber(res, "start_value")));
		s->maxvalue = strdup(PQgetvalue(res, 0, PQfnumber(res, "max_value")));
		s->minvalue = strdup(PQgetvalue(res, 0, PQfnumber(res, "min_value")));
		s->cache = strdup(PQgetvalue(res, 0, PQfnumber(res, "cache_value")));
		s->cycle = (PQgetvalue(res, 0, PQfnumber(res, "is_cycled"))[0] == 't');
	}

	free(query);
	PQclear(res);
}
コード例 #5
0
ファイル: trigger.c プロジェクト: eulerto/pgquarrel
PQLTrigger *
getTriggers(PGconn *c, int *n)
{
	PQLTrigger	*t;
	PGresult	*res;
	int			i;

	logNoise("trigger: server version: %d", PQserverVersion(c));

	res = PQexec(c,
				 "SELECT t.oid, t.tgname AS trgname, n.nspname AS nspname, c.relname AS relname, pg_get_triggerdef(t.oid, false) AS trgdef, obj_description(t.oid, 'pg_rewrite') AS description FROM pg_trigger t INNER JOIN pg_class c ON (t.tgrelid = c.oid) INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE NOT tgisinternal");

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	*n = PQntuples(res);
	if (*n > 0)
		t = (PQLTrigger *) malloc(*n * sizeof(PQLTrigger));
	else
		t = NULL;

	logDebug("number of triggers in server: %d", *n);

	for (i = 0; i < *n; i++)
	{
		t[i].oid = strtoul(PQgetvalue(res, i, PQfnumber(res, "oid")), NULL, 10);
		t[i].trgname = strdup(PQgetvalue(res, i, PQfnumber(res, "tgname")));
		t[i].table.schemaname = strdup(PQgetvalue(res, i, PQfnumber(res, "nspname")));
		t[i].table.objectname = strdup(PQgetvalue(res, i, PQfnumber(res, "relname")));
		t[i].trgdef = strdup(PQgetvalue(res, i, PQfnumber(res, "trgdef")));
		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			t[i].comment = NULL;
		else
			t[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));

		logDebug("trigger \"%s\" on \"%s\".\"%s\"", t[i].trgname, t[i].table.schemaname, t[i].table.objectname);
	}

	PQclear(res);

	return t;
}
コード例 #6
0
ファイル: rule.c プロジェクト: fabriziomello/pgquarrel
PQLRule *
getRules(PGconn *c, int *n)
{
	PQLRule		*r;
	PGresult	*res;
	int			i;

	logNoise("rule: server version: %d", PQserverVersion(c));

	res = PQexec(c,
				"SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition, obj_description(r.oid, 'pg_rewrite') AS description FROM pg_rewrite r INNER JOIN pg_class c ON (c.oid = r.ev_class) INNER JOIN pg_namespace n ON (n.oid = c.relnamespace) WHERE r.rulename <> '_RETURN'::name AND n.nspname !~ '^pg_' AND n.nspname <> 'information_schema' ORDER BY n.nspname, c.relname, r.rulename");

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	*n = PQntuples(res);
	if (*n > 0)
		r = (PQLRule *) malloc(*n * sizeof(PQLRule));
	else
		r = NULL;

	logDebug("number of rules in server: %d", *n);

	for (i = 0; i < *n; i++)
	{
		r[i].table.schemaname = strdup(PQgetvalue(res, i, PQfnumber(res, "schemaname")));
		r[i].table.objectname = strdup(PQgetvalue(res, i, PQfnumber(res, "tablename")));
		r[i].rulename = strdup(PQgetvalue(res, i, PQfnumber(res, "rulename")));
		r[i].ruledef = strdup(PQgetvalue(res, i, PQfnumber(res, "definition")));
		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			r[i].comment = NULL;
		else
			r[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));
		logDebug("rule %s on %s.%s", formatObjectIdentifier(r[i].rulename), formatObjectIdentifier(r[i].table.schemaname), formatObjectIdentifier(r[i].table.objectname));
	}

	PQclear(res);

	return r;
}
コード例 #7
0
ファイル: cast.c プロジェクト: eulerto/pgquarrel
PQLCast *
getCasts(PGconn *c, int *n)
{
	char		*query = NULL;
	int			nquery = PGQQRYLEN;
	PQLCast		*d;
	PGresult	*res;
	int			i;
	int			r;

	logNoise("cast: server version: %d", PQserverVersion(c));

	do
	{
		query = (char *) malloc(nquery * sizeof(char));

		if (PQserverVersion(c) >= 90100)	/* extension support */
		{
			r = snprintf(query, nquery,
					 "SELECT c.oid, format_type(c.castsource, t.typtypmod) as source, format_type(c.casttarget, u.typtypmod) as target, castmethod, quote_ident(n.nspname) || '.' || quote_ident(f.proname) || '(' || pg_get_function_arguments(f.oid) || ')' as funcname, castcontext, obj_description(c.oid, 'pg_cast') AS description FROM pg_cast c LEFT JOIN pg_type t ON (c.castsource = t.oid) LEFT JOIN pg_type u ON (c.casttarget = u.oid) LEFT JOIN pg_proc f ON (c.castfunc = f.oid) LEFT JOIN pg_namespace n ON (f.pronamespace = n.oid) WHERE c.oid >= %u AND NOT EXISTS(SELECT 1 FROM pg_depend d WHERE c.oid = d.objid AND d.deptype = 'e') ORDER BY source, target",
					 PGQ_FIRST_USER_OID);
		}
		else
		{
			r = snprintf(query, nquery,
					 "SELECT c.oid, format_type(c.castsource, t.typtypmod) as source, format_type(c.casttarget, u.typtypmod) as target, castmethod, quote_ident(n.nspname) || '.' || quote_ident(f.proname) || '(' || pg_get_function_arguments(f.oid) || ')' as funcname, castcontext, obj_description(c.oid, 'pg_cast') AS description FROM pg_cast c LEFT JOIN pg_type t ON (c.castsource = t.oid) LEFT JOIN pg_type u ON (c.casttarget = u.oid) LEFT JOIN pg_proc f ON (c.castfunc = f.oid) LEFT JOIN pg_namespace n ON (f.pronamespace = n.oid) WHERE c.oid >= %u ORDER BY source, target",
					 PGQ_FIRST_USER_OID);
		}

		if (r < nquery)
			break;

		logNoise("query size: required (%u) ; initial (%u)", r, nquery);
		nquery = r + 1;	/* make enough room for query */
		free(query);
	}
	while (true);

	res = PQexec(c, query);

	free(query);

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	*n = PQntuples(res);
	if (*n > 0)
		d = (PQLCast *) malloc(*n * sizeof(PQLCast));
	else
		d = NULL;

	logDebug("number of casts in server: %d", *n);

	for (i = 0; i < *n; i++)
	{
		d[i].oid = strtoul(PQgetvalue(res, i, PQfnumber(res, "oid")), NULL, 10);
		d[i].source = strdup(PQgetvalue(res, i, PQfnumber(res, "source")));
		d[i].target = strdup(PQgetvalue(res, i, PQfnumber(res, "target")));
		d[i].method = PQgetvalue(res, i, PQfnumber(res, "castmethod"))[0];
		d[i].funcname = strdup(PQgetvalue(res, i, PQfnumber(res, "funcname")));
		d[i].context = PQgetvalue(res, i, PQfnumber(res, "castcontext"))[0];

		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			d[i].comment = NULL;
		else
			d[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));

		logDebug("cast \"%s\" as \"%s\" ; method: %c ; context: %c", d[i].source,
				 d[i].target, d[i].method, d[i].context);
	}

	PQclear(res);

	return d;
}
コード例 #8
0
ファイル: common.c プロジェクト: eulerto/pgquarrel
/*
 * Return an allocated string that contains comma-separated options. Return
 * NULL if linked list is NULL.
 */
char *
printOptions(stringList *sl)
{
	char			*list = NULL;
	stringListCell	*p;
	bool			firstitem = true;

	if (sl)
	{
		int		listlen;
		int		n = 0;

		/* allocate memory for at list one parameter (based on autovac parameters) */
		listlen = 40;
		list = (char *) malloc(listlen * sizeof(char));

		/* build a list like 'a=10, b=20, c=30' or 'a, b, c' */
		for (p = sl->head; p; p = p->next)
		{
			int	newlen;

			/*
			 * String space including new option and separator. Don't forget
			 * the null-character.
			 */
			newlen = n + strlen(p->value) + 2 + 1;
			if (newlen > listlen)
			{
				logNoise("allocate more memory (was %d ; is %d)", listlen, newlen);

				listlen = newlen;
				list = (char *) realloc(list, listlen);
				if (list == NULL)
				{
					logError("could not allocate memory");
					exit(EXIT_FAILURE);
				}
			}

			if (firstitem)
			{
				firstitem = false;
			}
			else
			{
				/*
				 * If it is not the first item, add comma and space before it.
				 * Copy 3 characters from destination to source, although only
				 * 2 (comma and space) will be added to list. Don't forget the
				 * null-character.
				 */
				strncpy(list + n, ", ", 3);
				n += 2;
			}

			strcpy(list + n, p->value);
			n += strlen(p->value);
		}
	}

	if (list)
		logNoise("options: %s", list);

	return list;
}
コード例 #9
0
ファイル: common.c プロジェクト: eulerto/pgquarrel
/*
 * Build an ordered linked list from a comma-separated string. If there is no
 * options return NULL.
 */
stringList *
buildStringList(char *options)
{
	stringList		*sl;
	stringListCell	*x;
	char			*tmp;
	char			*p;

	char			*item;
	char			*nextitem;

	sl = (stringList *) malloc(sizeof(stringList));
	sl->head = sl->tail = NULL;

	/* no options, bail out */
	if (options == NULL)
	{
		logDebug("options is empty");
		return sl;
	}

	tmp = strdup(options);
	p = tmp;

	for (item = p; item; item = nextitem)
	{
		stringListCell	*sc;

		nextitem = strchr(item, ',');
		if (nextitem)
			*nextitem++ = '\0';

		/* left trim */
		while (isspace(*item))
			item++;

		sc = (stringListCell *) malloc(sizeof(stringListCell));
		sc->value = strdup(item);
		sc->next = NULL;

		logNoise("option: \"%s\"", item);

		/* add stringListCell to stringList in order */
		if (sl->tail)
		{
			stringListCell	*cur = sl->head;

			if (strcmp(cur->value, sc->value) > 0)
			{
				sc->next = cur;
				sl->head = sc;
			}
			else
			{
				while (cur != NULL)
				{
					if (cur == sl->tail)
					{
						cur->next = sc;
						sl->tail = sc;
						break;
					}

					if (strcmp(cur->value, sc->value) < 0 &&
							strcmp(cur->next->value, sc->value) >= 0)
					{
						sc->next = cur->next;
						cur->next = sc;
						break;
					}

					cur = cur->next;
				}
			}
		}
		else
		{
			sl->head = sc;
			sl->tail = sc;
		}
	}

	/* check the order */
	for (x = sl->head; x; x = x->next)
		logNoise("options in order: \"%s\"", x->value);

	free(tmp);

	return sl;
}
コード例 #10
0
ファイル: language.c プロジェクト: eulerto/pgquarrel
PQLLanguage *
getLanguages(PGconn *c, int *n)
{
	PQLLanguage	*l;
	PGresult	*res;
	int			i;

	logNoise("language: server version: %d", PQserverVersion(c));

	if (PQserverVersion(c) >= 90100)	/* extension support */
	{
		res = PQexec(c,
				 "SELECT l.oid, lanname AS languagename, CASE WHEN tmplname IS NULL THEN false ELSE true END AS pltemplate, lanpltrusted AS trusted, p1.proname AS callhandler, p2.proname AS inlinehandler, p3.proname AS validator, obj_description(l.oid, 'pg_language') AS description, pg_get_userbyid(lanowner) AS lanowner, lanacl FROM pg_language l LEFT JOIN pg_pltemplate t ON (l.lanname = t.tmplname) LEFT JOIN pg_proc p1 ON (p1.oid = lanplcallfoid) LEFT JOIN pg_proc p2 ON (p2.oid = laninline) LEFT JOIN pg_proc p3 ON (p3.oid = lanvalidator) WHERE lanispl AND NOT EXISTS(SELECT 1 FROM pg_depend d WHERE l.oid = d.objid AND d.deptype = 'e') ORDER BY lanname");
	}
	else
	{
		res = PQexec(c,
				 "SELECT l.oid, lanname AS languagename, CASE WHEN tmplname IS NULL THEN false ELSE true END AS pltemplate, lanpltrusted AS trusted, p1.proname AS callhandler, p2.proname AS inlinehandler, p3.proname AS validator, obj_description(l.oid, 'pg_language') AS description, pg_get_userbyid(lanowner) AS lanowner, lanacl FROM pg_language l LEFT JOIN pg_pltemplate t ON (l.lanname = t.tmplname) LEFT JOIN pg_proc p1 ON (p1.oid = lanplcallfoid) LEFT JOIN pg_proc p2 ON (p2.oid = laninline) LEFT JOIN pg_proc p3 ON (p3.oid = lanvalidator) WHERE lanispl ORDER BY lanname");
	}

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	*n = PQntuples(res);
	if (*n > 0)
		l = (PQLLanguage *) malloc(*n * sizeof(PQLLanguage));
	else
		l = NULL;

	logDebug("number of languages in server: %d", *n);

	for (i = 0; i < *n; i++)
	{
		l[i].oid = strtoul(PQgetvalue(res, i, PQfnumber(res, "oid")), NULL, 10);
		l[i].languagename = strdup(PQgetvalue(res, i, PQfnumber(res, "languagename")));
		l[i].pltemplate = (PQgetvalue(res, i, PQfnumber(res, "pltemplate"))[0] == 't');
		l[i].trusted = (PQgetvalue(res, i, PQfnumber(res, "trusted"))[0] == 't');
		l[i].callhandler = strdup(PQgetvalue(res, i, PQfnumber(res, "callhandler")));
		l[i].inlinehandler = strdup(PQgetvalue(res, i, PQfnumber(res,
											   "inlinehandler")));
		l[i].validator = strdup(PQgetvalue(res, i, PQfnumber(res, "validator")));
		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			l[i].comment = NULL;
		else
			l[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));

		l[i].owner = strdup(PQgetvalue(res, i, PQfnumber(res, "lanowner")));
		if (PQgetisnull(res, i, PQfnumber(res, "lanacl")))
			l[i].acl = NULL;
		else
			l[i].acl = strdup(PQgetvalue(res, i, PQfnumber(res, "lanacl")));

		/*
		 * Security labels are not assigned here (see
		 * getLanguageSecurityLabels), but default values are essential to
		 * avoid having trouble in freeLanguages.
		 */
		l[i].nseclabels = 0;
		l[i].seclabels = NULL;

		logDebug("language \"%s\"", l[i].languagename);
	}

	PQclear(res);

	return l;
}
コード例 #11
0
ファイル: collation.c プロジェクト: eulerto/pgquarrel
PQLCollation *
getCollations(PGconn *c, int *n)
{
	char			*query = NULL;
	int				nquery = PGQQRYLEN;
	PQLCollation	*d;
	PGresult		*res;
	int				i;
	int				r;

	logNoise("collation: server version: %d", PQserverVersion(c));

	/* bail out if we do not support it */
	if (PQserverVersion(c) < 90100)
	{
		logWarning("ignoring collations because server does not support it");
		return NULL;
	}

	do
	{
		query = (char *) malloc(nquery * sizeof(char));

		r = snprintf(query, nquery,
					 "SELECT c.oid, n.nspname, collname, pg_encoding_to_char(collencoding) AS collencoding, collcollate, collctype, pg_get_userbyid(collowner) AS collowner, obj_description(c.oid, 'pg_collation') AS description FROM pg_collation c INNER JOIN pg_namespace n ON (c.collnamespace = n.oid) WHERE c.oid >= %u AND NOT EXISTS(SELECT 1 FROM pg_depend d WHERE c.oid = d.objid AND d.deptype = 'e') ORDER BY n.nspname, collname",
					 PGQ_FIRST_USER_OID);

		if (r < nquery)
			break;

		logNoise("query size: required (%u) ; initial (%u)", r, nquery);
		nquery = r + 1;	/* make enough room for query */
		free(query);
	}
	while (true);

	res = PQexec(c, query);

	free(query);

	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		logError("query failed: %s", PQresultErrorMessage(res));
		PQclear(res);
		PQfinish(c);
		/* XXX leak another connection? */
		exit(EXIT_FAILURE);
	}

	*n = PQntuples(res);
	if (*n > 0)
		d = (PQLCollation *) malloc(*n * sizeof(PQLCollation));
	else
		d = NULL;

	logDebug("number of collations in server: %d", *n);

	for (i = 0; i < *n; i++)
	{
		d[i].obj.oid = strtoul(PQgetvalue(res, i, PQfnumber(res, "oid")), NULL, 10);
		d[i].obj.schemaname = strdup(PQgetvalue(res, i, PQfnumber(res, "nspname")));
		d[i].obj.objectname = strdup(PQgetvalue(res, i, PQfnumber(res, "collname")));
		d[i].encoding = strdup(PQgetvalue(res, i, PQfnumber(res, "collencoding")));
		d[i].collate = strdup(PQgetvalue(res, i, PQfnumber(res, "collcollate")));
		d[i].ctype = strdup(PQgetvalue(res, i, PQfnumber(res, "collctype")));

		if (PQgetisnull(res, i, PQfnumber(res, "description")))
			d[i].comment = NULL;
		else
			d[i].comment = strdup(PQgetvalue(res, i, PQfnumber(res, "description")));

		d[i].owner = strdup(PQgetvalue(res, i, PQfnumber(res, "collowner")));

		logDebug("collation \"%s\".\"%s\"", d[i].obj.schemaname, d[i].obj.objectname);
	}

	PQclear(res);

	return d;
}