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; }
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; }
/* * 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; }
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); }
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; }
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; }
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; }
/* * 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; }
/* * 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; }
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; }
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; }