diff -Naur postgresql-7.4.1.distrib/src/bin/psql/command.c postgresql-7.4.1.patched/src/bin/psql/command.c --- postgresql-7.4.1.distrib/src/bin/psql/command.c Sat Oct 11 11:04:26 2003 +++ postgresql-7.4.1.patched/src/bin/psql/command.c Mon Jan 26 20:28:31 2004 @@ -520,6 +520,16 @@ } + /* \M XML mode */ + else if (strcmp(cmd, "M") == 0 || strcmp(cmd, "xml") == 0) + { + if (pset.popt.topt.format != PRINT_XML) + success = do_pset("format", "xml", &pset.popt, quiet); + else + success = do_pset("format", "aligned", &pset.popt, quiet); + } + + /* \i is include file */ else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0) { @@ -1742,6 +1752,9 @@ case PRINT_HTML: return "html"; break; + case PRINT_XML: + return "xml"; + break; case PRINT_LATEX: return "latex"; break; @@ -1776,11 +1789,13 @@ popt->topt.format = PRINT_ALIGNED; else if (strncasecmp("html", value, vallen) == 0) popt->topt.format = PRINT_HTML; + else if (strncasecmp("xml", value, vallen) == 0) + popt->topt.format = PRINT_XML; else if (strncasecmp("latex", value, vallen) == 0) popt->topt.format = PRINT_LATEX; else { - psql_error("\\pset: allowed formats are unaligned, aligned, html, latex\n"); + psql_error("\\pset: allowed formats are unaligned, aligned, html, xml, latex\n"); return false; } diff -Naur postgresql-7.4.1.distrib/src/bin/psql/help.c postgresql-7.4.1.patched/src/bin/psql/help.c --- postgresql-7.4.1.distrib/src/bin/psql/help.c Wed Oct 1 23:39:31 2003 +++ postgresql-7.4.1.patched/src/bin/psql/help.c Mon Jan 26 20:23:51 2004 @@ -112,6 +112,7 @@ puts(_("\nOutput format options:")); puts(_(" -A unaligned table output mode (-P format=unaligned)")); puts(_(" -H HTML table output mode (-P format=html)")); + puts(_(" -M XML output mode")); puts(_(" -t print rows only (-P tuples_only)")); puts(_(" -T TEXT set HTML table tag attributes (width, border) (-P tableattr=)")); puts(_(" -x turn on expanded table output (-P expanded)")); @@ -232,6 +233,8 @@ fprintf(output, _(" \\f [STRING] show or set field separator for unaligned query output\n")); fprintf(output, _(" \\H toggle HTML output mode (currently %s)\n"), ON(pset.popt.topt.format == PRINT_HTML)); + fprintf(output, _(" \\M toggle XML output mode (currently %s)\n"), + ON(pset.popt.topt.format == PRINT_XML)); fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" diff -Naur postgresql-7.4.1.distrib/src/bin/psql/print.c postgresql-7.4.1.patched/src/bin/psql/print.c --- postgresql-7.4.1.distrib/src/bin/psql/print.c Thu Aug 14 11:49:42 2003 +++ postgresql-7.4.1.patched/src/bin/psql/print.c Mon Jan 26 20:18:42 2004 @@ -1219,10 +1219,23 @@ /* call table printer */ - printTable(opt->title, headers, cells, - footers ? (const char *const *) footers : (const char *const *) (opt->footers), - align, &opt->topt, fout); + switch(opt->topt.format) { + case PRINT_XML: + { + const char *s; + + s = PGresult_as_xml(result, 1); + fprintf(fout, "%s\n", s); + free(s); + } + break; + default: + printTable(opt->title, headers, cells, + footers ? (const char *const *) footers : (const char *const *) (opt->footers), + align, &opt->topt, fout); + break; + } free((void *) headers); free((void *) cells); if (footers) diff -Naur postgresql-7.4.1.distrib/src/bin/psql/print.h postgresql-7.4.1.patched/src/bin/psql/print.h --- postgresql-7.4.1.distrib/src/bin/psql/print.h Mon Aug 4 16:59:40 2003 +++ postgresql-7.4.1.patched/src/bin/psql/print.h Mon Jan 26 19:19:12 2004 @@ -21,6 +21,7 @@ PRINT_UNALIGNED, PRINT_ALIGNED, PRINT_HTML, + PRINT_XML, PRINT_LATEX /* add your favourite output format here ... */ }; diff -Naur postgresql-7.4.1.distrib/src/bin/psql/startup.c postgresql-7.4.1.patched/src/bin/psql/startup.c --- postgresql-7.4.1.distrib/src/bin/psql/startup.c Mon Sep 29 11:21:33 2003 +++ postgresql-7.4.1.patched/src/bin/psql/startup.c Mon Jan 26 20:40:41 2004 @@ -322,6 +322,7 @@ {"field-separator", required_argument, NULL, 'F'}, {"host", required_argument, NULL, 'h'}, {"html", no_argument, NULL, 'H'}, + {"xml", no_argument, NULL, 'M'}, {"list", no_argument, NULL, 'l'}, {"no-readline", no_argument, NULL, 'n'}, {"output", required_argument, NULL, 'o'}, @@ -352,7 +353,7 @@ memset(options, 0, sizeof *options); - while ((c = getopt_long(argc, argv, "aAc:d:eEf:F:h:Hlno:p:P:qR:sStT:uU:v:VWxX?", + while ((c = getopt_long(argc, argv, "aAc:d:eEf:F:h:HlMno:p:P:qR:sStT:uU:v:VWxX?", long_options, &optindex)) != -1) { switch (c) @@ -395,6 +396,9 @@ case 'H': pset.popt.topt.format = PRINT_HTML; break; + case 'M': + pset.popt.topt.format = PRINT_XML; + break; case 'l': options->action = ACT_LIST_DB; break; diff -Naur postgresql-7.4.1.distrib/src/interfaces/libpq/Makefile postgresql-7.4.1.patched/src/interfaces/libpq/Makefile --- postgresql-7.4.1.distrib/src/interfaces/libpq/Makefile Sat Sep 27 08:32:48 2003 +++ postgresql-7.4.1.patched/src/interfaces/libpq/Makefile Mon Jan 26 09:24:43 2004 @@ -20,7 +20,7 @@ override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) $(THREAD_CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"' -OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ +OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-printxml.o fe-lobj.o \ fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \ dllist.o md5.o ip.o wchar.o encnames.o \ $(filter crypt.o getaddrinfo.o inet_aton.o snprintf.o strerror.o path.o thread.o, $(LIBOBJS)) diff -Naur postgresql-7.4.1.distrib/src/interfaces/libpq/fe-printxml.c postgresql-7.4.1.patched/src/interfaces/libpq/fe-printxml.c --- postgresql-7.4.1.distrib/src/interfaces/libpq/fe-printxml.c Wed Dec 31 16:00:00 1969 +++ postgresql-7.4.1.patched/src/interfaces/libpq/fe-printxml.c Mon Jan 26 20:13:44 2004 @@ -0,0 +1,1443 @@ +#include "libpq-fe.h" +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct libmooreb_strbuf_tag libmooreb_strbuf_t; + +static libmooreb_strbuf_t *libmooreb_strbuf_create(size_t initial_capacity); +static void libmooreb_strbuf_destroy(libmooreb_strbuf_t *s); +static int libmooreb_strbuf_concat(libmooreb_strbuf_t *s, const char *p, size_t p_len); +static char *libmooreb_strbuf_finish(libmooreb_strbuf_t *s); + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +struct libmooreb_strbuf_tag { + char *buf; + size_t initial_capacity; + size_t num_times_realloced; + size_t capacity; + size_t capacity_remaining; +}; + +static libmooreb_strbuf_t *libmooreb_strbuf_create(size_t initial_capacity) { + libmooreb_strbuf_t *retval = NULL; + + retval = (libmooreb_strbuf_t *)malloc(sizeof(libmooreb_strbuf_t)); + if(retval) { + retval->buf = (char *)malloc(initial_capacity*sizeof(char)); + if(retval->buf) { + (retval->buf)[0] = '\0'; + retval->initial_capacity = initial_capacity; + retval->num_times_realloced = 0; + retval->capacity = initial_capacity; + retval->capacity_remaining = initial_capacity - 1; + } + else { + /* could not allocate stringbuf */ + free(retval); + retval = NULL; + } + } + return(retval); +} + +static void libmooreb_strbuf_destroy(libmooreb_strbuf_t *s) { + if(s) { + free(s->buf); + free(s); + } + return; +} + +/* pass the size of p *including* the null byte */ +/* return 0 for success, non-zero for error: + * 1 for could not realloc. + * 2 for p_len < 0 + * s is not touched in the error case. + */ +static int libmooreb_strbuf_concat(libmooreb_strbuf_t *s, const char *p, size_t p_len) { + int retval = 0; + size_t insertion_point_offset; + + if(p_len < 1) { + return 2; + } + + insertion_point_offset = s->capacity-s->capacity_remaining-1; + if(p_len <= (s->capacity_remaining+1)) { + /* no need to realloc the buf: there's enough room for this string. */ + assert('\0' == s->buf[insertion_point_offset]); + strncpy((s->buf)+insertion_point_offset, p, p_len); + s->capacity_remaining -= (p_len - 1); + } + else { + /* ran out of room in the buf. we need to realloc. the question is, how much? */ + void *r_retval; + size_t additional_capacity; + size_t new_capacity; + + additional_capacity = MAX(s->initial_capacity, p_len); + new_capacity = additional_capacity + s->capacity; + + r_retval = realloc(s->buf, new_capacity); + if(r_retval) { + s->buf = r_retval; + s->capacity = new_capacity; + s->capacity_remaining += additional_capacity - p_len + 1; + s->num_times_realloced++; + assert('\0' == s->buf[insertion_point_offset]); + strncpy((s->buf)+insertion_point_offset, p, p_len); + } + else { + /* realloc failed. */ + /* leave s alone and leave the caller to deal */ + retval = 1; + } + } + return(retval); +} + + +/* deallocate the strbuf and return the constructed string. + * it's now the caller's responsibility to free the string + */ +static char *libmooreb_strbuf_finish(libmooreb_strbuf_t *s) { + char *retval = NULL; + if(s) { + retval = s->buf; + free(s); + } + return(retval); +} + +/** + * @File libmooreb_hash.c + * @author Kaz Kylheku, Brian M. Moore + * @date Sun Aug 18 13:30:24 2002 + * + * @brief Contains the libmooreb definition of the hashtable ADT. + * + * + */ +/* This code, originally by kaz, has been modified to meet the needs + * of libmooreb. It is a derivative work, by kaz's definition. + * We have merged up all of kazlib's following revisions: + * * kazlib 1.20 + * In particular, the following changes have been made: + * Changes: + * * Brian Moore, Jul. 2002: Removed obsolete functions. + * * Brian Moore, Jul. 2002: Removed macros and actually made + * the data structures opaque. This allowed removal of side-effect + * and opaque checks and restrictions. + * * Brian Moore, Jul. 2002: Placed kazlib in libmooreb namespace by + * prefixing all functions with libmooreb_. + * * Brian Moore, Jan. 2002: Threaded the libraries with an implicit lock. + * * Initial revision (pre Jan. 2002): kazlib 1.20. + */ + +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + */ + +#define LIBMOOREB_HASH_NOLOCK 1 +#define LIBMOOREB_HASH_LOCK 1 + +typedef unsigned long libmooreb_hashcount_t; +#define LIBMOOREB_HASHCOUNT_T_MAX ULONG_MAX + +typedef unsigned long libmooreb_hash_val_t; +#define LIBMOOREB_HASH_VAL_T_MAX ULONG_MAX + +#ifndef LIBMOOREB_HASH_VAL_T_BIT +#define LIBMOOREB_HASH_VAL_T_BIT ((int) libmooreb_hash_val_t_bit) +#endif + +/** + * libmooreb_hash_free_fun_t exists to free outside-allocated keys or values in hnodes + */ +typedef void (*libmooreb_hash_free_fun_t)(void *); + +/** + * libmooreb_hnode_t is an opaque type + */ +typedef struct libmooreb_hnode_tag libmooreb_hnode_t; + +/* + * The comparison function pointer type. A comparison function takes two keys + * and produces a value of -1 if the left key is less than the right key, a + * value of 0 if the keys are equal, and a value of 1 if the left key is + * greater than the right key. + */ + +typedef int (*libmooreb_hash_comp_t)(const void *, const void *); + +/* + * The hashing function performs some computation on a key and produces an + * integral value of type hash_val_t based on that key. For best results, the + * function should have a good randomness properties in *all* significant bits + * over the set of keys that are being inserted into a given hash table. In + * particular, the most significant bits of hash_val_t are most significant to + * the hash module. Only as the hash table expands are less significant bits + * examined. Thus a function that has good distribution in its upper bits but + * not lower is preferrable to one that has poor distribution in the upper bits + * but not the lower ones. + */ + +typedef libmooreb_hash_val_t (*libmooreb_hash_fun_t)(const void *); + +/** + * libmooreb_hash_t is an opaque type. + */ +typedef struct libmooreb_hash_tag libmooreb_hash_t; + +/** + * libmooreb_hscan_t is an opaque type. + */ +typedef struct libmooreb_hscan_tag libmooreb_hscan_t; + +static libmooreb_hash_t *libmooreb_hash_create(libmooreb_hashcount_t, libmooreb_hash_comp_t, libmooreb_hash_fun_t, int implicit_lock); +static void libmooreb_hash_destroy(libmooreb_hash_t *); +static void libmooreb_hash_free_nodes(libmooreb_hash_t *); +static libmooreb_hnode_t *libmooreb_hash_lookup(libmooreb_hash_t *, const void *); +static int libmooreb_hash_alloc_insert(libmooreb_hash_t *, const void *, void *); + +static void *libmooreb_hnode_get(libmooreb_hnode_t *); +static const void *libmooreb_hnode_getkey(libmooreb_hnode_t *); + +static void libmooreb_hash_scan_begin(libmooreb_hscan_t *, libmooreb_hash_t *); +static libmooreb_hnode_t *libmooreb_hash_scan_next(libmooreb_hscan_t *); + +static libmooreb_hscan_t *libmooreb_hscan_create(void); +static void libmooreb_hscan_destroy(libmooreb_hscan_t *x); + + +static void libmooreb_hash_set_val_free_func(libmooreb_hash_t *hash, libmooreb_hash_free_fun_t f); + +static const char rcsid[] = "$Id: libmooreb_hash.c,v 1.11 2004/01/22 05:00:57 mooreb Exp $"; + +/* + * Hash chain node structure. + * Notes: + * 1. This is a pointer to the next node in the chain. In the last node of a + * chain, this pointer is null. + * 2. The key is a pointer to some user supplied data that contains a unique + * identifier for each hash node in a given table. The interpretation of + * the data is up to the user. When creating or initializing a hash table, + * the user must supply a pointer to a function for comparing two keys, + * and a pointer to a function for hashing a key into a numeric value. + * 3. The value is a user-supplied pointer to void which may refer to + * any data object. It is not interpreted in any way by the hashing + * module. + * 4. The hashed key is stored in each node so that we don't have to rehash + * each key when the table must grow or shrink. + */ + +struct libmooreb_hnode_tag { + struct libmooreb_hnode_tag *hash_next; /* 1 */ + const void *hash_key; /* 2 */ + void *hash_data; /* 3 */ + libmooreb_hash_val_t hash_hkey; /* 4 */ +}; + +/* + * This is the hash table control structure. It keeps track of information + * about a hash table, as well as the hash table itself. + * Notes: + * 1. Pointer to the hash table proper. The table is an array of pointers to + * hash nodes (of type hnode_t). If the table is empty, every element of + * this table is a null pointer. A non-null entry points to the first + * element of a chain of nodes. + * 2. This member keeps track of the size of the hash table---that is, the + * number of chain pointers. + * 3. The count member maintains the number of elements that are presently + * in the hash table. + * 4. The maximum count is the greatest number of nodes that can populate this + * table. If the table contains this many nodes, no more can be inserted, + * and the hash_isfull() function returns true. + * 5. The high mark is a population threshold, measured as a number of nodes, + * which, if exceeded, will trigger a table expansion. Only dynamic hash + * tables are subject to this expansion. + * 6. The low mark is a minimum population threshold, measured as a number of + * nodes. If the table population drops below this value, a table shrinkage + * will occur. Only dynamic tables are subject to this reduction. No table + * will shrink beneath a certain absolute minimum number of nodes. + * 7. This is the a pointer to the hash table's comparison function. The + * function is set once at initialization or creation time. + * 8. Pointer to the table's hashing function, set once at creation or + * initialization time. + * 9. The current hash table mask. If the size of the hash table is 2^N, + * this value has its low N bits set to 1, and the others clear. It is used + * to select bits from the result of the hashing function to compute an + * index into the table. + * 10. A flag which indicates whether the table is to be dynamically resized. It + * is set to 1 in dynamically allocated tables, 0 in tables that are + * statically allocated. + */ + +struct libmooreb_hash_tag { + struct libmooreb_hnode_tag **hash_table; /* 1 */ + libmooreb_hashcount_t hash_nchains; /* 2 */ + libmooreb_hashcount_t hash_nodecount; /* 3 */ + libmooreb_hashcount_t hash_maxcount; /* 4 */ + libmooreb_hashcount_t hash_highmark; /* 5 */ + libmooreb_hashcount_t hash_lowmark; /* 6 */ + libmooreb_hash_comp_t hash_compare; /* 7 */ + libmooreb_hash_fun_t hash_function; /* 8 */ + libmooreb_hash_val_t hash_mask; /* 9 */ + libmooreb_hash_free_fun_t key_free_fun; + libmooreb_hash_free_fun_t val_free_fun; + int hash_dynamic; /* 10 */ + int implicit_lock; +}; + + +/* + * Hash scanner structure, used for traversals of the data structure. + * Notes: + * 1. Pointer to the hash table that is being traversed. + * 2. Reference to the current chain in the table being traversed (the chain + * that contains the next node that shall be retrieved). + * 3. Pointer to the node that will be retrieved by the subsequent call to + * hash_scan_next(). + */ + +struct libmooreb_hscan_tag { + libmooreb_hash_t *hash_table; /* 1 */ + libmooreb_hash_val_t hash_chain; /* 2 */ + libmooreb_hnode_t *hash_next; /* 3 */ +}; + +/* local static prototypes */ +static int libmooreb_hash_isempty_NOLOCK(libmooreb_hash_t *hash); +static libmooreb_hash_t *libmooreb_hash_create_NOLOCK(libmooreb_hashcount_t maxcount, libmooreb_hash_comp_t compfun, libmooreb_hash_fun_t hashfun); +static void libmooreb_hash_free_nodes_NOLOCK(libmooreb_hash_t *hash); +static void libmooreb_hash_destroy_NOLOCK(libmooreb_hash_t *hash); +static void libmooreb_hash_scan_begin_NOLOCK(libmooreb_hscan_t *scan, libmooreb_hash_t *hash); +static libmooreb_hnode_t *libmooreb_hash_scan_next_NOLOCK(libmooreb_hscan_t *scan); +static libmooreb_hnode_t *libmooreb_hash_lookup_NOLOCK(libmooreb_hash_t *hash, const void *key); +static int libmooreb_hash_alloc_insert_NOLOCK(libmooreb_hash_t *hash, const void *key, void *data); +static libmooreb_hnode_t *libmooreb_hash_scan_delete_NOLOCK(libmooreb_hash_t *hash, libmooreb_hnode_t *node); +static int libmooreb_hash_verify_NOLOCK(libmooreb_hash_t *hash); +static libmooreb_hnode_t *libmooreb_hnode_init_NOLOCK(libmooreb_hnode_t *hnode, void *data); +static void *libmooreb_hnode_get_NOLOCK(libmooreb_hnode_t *node); +static const void *libmooreb_hnode_getkey_NOLOCK(libmooreb_hnode_t *node); +static void *libmooreb_hnode_get_NOLOCK(libmooreb_hnode_t *node); + +#define INIT_BITS 6 +#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ +#define INIT_MASK ((INIT_SIZE) - 1) + +static libmooreb_hnode_t *libmooreb_hnode_alloc(void); +static void libmooreb_hnode_free(libmooreb_hash_t *hash, libmooreb_hnode_t *node); +static libmooreb_hash_val_t libmooreb_hash_fun_default(const void *key); +static int libmooreb_hash_comp_default(const void *key1, const void *key2); + +static int libmooreb_hash_val_t_bit; + +/* + * Compute the number of bits in the hash_val_t type. We know that hash_val_t + * is an unsigned integral type. Thus the highest value it can hold is a + * Mersenne number (power of two, less one). We initialize a hash_val_t + * object with this value and then shift bits out one by one while counting. + * Notes: + * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power + * of two. This means that its binary representation consists of all one + * bits, and hence ``val'' is initialized to all one bits. + * 2. While bits remain in val, we increment the bit count and shift it to the + * right, replacing the topmost bit by zero. + */ + +static void compute_bits(void) +{ + libmooreb_hash_val_t val = LIBMOOREB_HASH_VAL_T_MAX; /* 1 */ + int bits = 0; + + while (val) { /* 2 */ + bits++; + val >>= 1; + } + + libmooreb_hash_val_t_bit = bits; +} + +/* + * Verify whether the given argument is a power of two. + */ + +static int is_power_of_two(libmooreb_hash_val_t arg) +{ + if (arg == 0) + return 0; + while ((arg & 1) == 0) + arg >>= 1; + return (arg == 1); +} + +/* + * Initialize the table of pointers to null. + */ + +static void clear_table(libmooreb_hash_t *hash) +{ + libmooreb_hash_val_t i; + + for (i = 0; i < hash->hash_nchains; i++) + hash->hash_table[i] = NULL; +} + +/* + * Double the size of a dynamic table. This works as follows. Each chain splits + * into two adjacent chains. The shift amount increases by one, exposing an + * additional bit of each hashed key. For each node in the original chain, the + * value of this newly exposed bit will decide which of the two new chains will + * receive the node: if the bit is 1, the chain with the higher index will have + * the node, otherwise the lower chain will receive the node. In this manner, + * the hash table will continue to function exactly as before without having to + * rehash any of the keys. + * Notes: + * 1. Overflow check. + * 2. The new number of chains is twice the old number of chains. + * 3. The new mask is one bit wider than the previous, revealing a + * new bit in all hashed keys. + * 4. Allocate a new table of chain pointers that is twice as large as the + * previous one. + * 5. If the reallocation was successful, we perform the rest of the growth + * algorithm, otherwise we do nothing. + * 6. The exposed_bit variable holds a mask with which each hashed key can be + * AND-ed to test the value of its newly exposed bit. + * 7. Now loop over each chain in the table and sort its nodes into two + * chains based on the value of each node's newly exposed hash bit. + * 8. The low chain replaces the current chain. The high chain goes + * into the corresponding sister chain in the upper half of the table. + * 9. We have finished dealing with the chains and nodes. We now update + * the various bookeeping fields of the hash structure. + */ + +static void grow_table(libmooreb_hash_t *hash) +{ + libmooreb_hnode_t **newtable; + + assert (2 * hash->hash_nchains > hash->hash_nchains); /* 1 */ + + newtable = realloc(hash->hash_table, + sizeof *newtable * hash->hash_nchains * 2); /* 4 */ + + if (newtable) { /* 5 */ + libmooreb_hash_val_t mask = (hash->hash_mask << 1) | 1; /* 3 */ + libmooreb_hash_val_t exposed_bit = mask ^ hash->hash_mask; /* 6 */ + libmooreb_hash_val_t chain; + + assert (mask != hash->hash_mask); + + for (chain = 0; chain < hash->hash_nchains; chain++) { /* 7 */ + libmooreb_hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; + + for (hptr = newtable[chain]; hptr != 0; hptr = next) { + next = hptr->hash_next; + + if (hptr->hash_hkey & exposed_bit) { + hptr->hash_next = high_chain; + high_chain = hptr; + } else { + hptr->hash_next = low_chain; + low_chain = hptr; + } + } + + newtable[chain] = low_chain; /* 8 */ + newtable[chain + hash->hash_nchains] = high_chain; + } + + hash->hash_table = newtable; /* 9 */ + hash->hash_mask = mask; + hash->hash_nchains *= 2; + hash->hash_lowmark *= 2; + hash->hash_highmark *= 2; + } + assert (libmooreb_hash_verify_NOLOCK(hash)); +} + +static libmooreb_hscan_t *libmooreb_hscan_create(void) { + return((libmooreb_hscan_t *)malloc(sizeof(libmooreb_hscan_t))); +} + +static void libmooreb_hscan_destroy(libmooreb_hscan_t *x) { + free(x); +} + +/* + * Create a dynamic hash table. Both the hash table structure and the table + * itself are dynamically allocated. Furthermore, the table is extendible in + * that it will automatically grow as its load factor increases beyond a + * certain threshold. + * Notes: + * 1. If the number of bits in the hash_val_t type has not been computed yet, + * we do so here, because this is likely to be the first function that the + * user calls. + * 2. Allocate a hash table control structure. + * 3. If a hash table control structure is successfully allocated, we + * proceed to initialize it. Otherwise we return a null pointer. + * 4. We try to allocate the table of hash chains. + * 5. If we were able to allocate the hash chain table, we can finish + * initializing the hash structure and the table. Otherwise, we must + * backtrack by freeing the hash structure. + * 6. INIT_SIZE should be a power of two. The high and low marks are always set + * to be twice the table size and half the table size respectively. When the + * number of nodes in the table grows beyond the high size (beyond load + * factor 2), it will double in size to cut the load factor down to about + * about 1. If the table shrinks down to or beneath load factor 0.5, + * it will shrink, bringing the load up to about 1. However, the table + * will never shrink beneath INIT_SIZE even if it's emptied. + * 7. This indicates that the table is dynamically allocated and dynamically + * resized on the fly. A table that has this value set to zero is + * assumed to be statically allocated and will not be resized. + * 8. The table of chains must be properly reset to all null pointers. + */ + +static libmooreb_hash_t *libmooreb_hash_create_NOLOCK(libmooreb_hashcount_t maxcount, libmooreb_hash_comp_t compfun, + libmooreb_hash_fun_t hashfun) +{ + libmooreb_hash_t *hash; + + if (libmooreb_hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + hash = malloc(sizeof *hash); /* 2 */ + + if (hash) { /* 3 */ + hash->hash_table = malloc(sizeof *hash->hash_table * INIT_SIZE); /* 4 */ + if (hash->hash_table) { /* 5 */ + hash->hash_nchains = INIT_SIZE; /* 6 */ + hash->hash_highmark = INIT_SIZE * 2; + hash->hash_lowmark = INIT_SIZE / 2; + hash->hash_nodecount = 0; + hash->hash_maxcount = maxcount; + hash->hash_compare = compfun ? compfun : libmooreb_hash_comp_default; + hash->hash_function = hashfun ? hashfun : libmooreb_hash_fun_default; + hash->hash_mask = INIT_MASK; + hash->key_free_fun = NULL; + hash->val_free_fun = NULL; + hash->hash_dynamic = 1; /* 7 */ + clear_table(hash); /* 8 */ + assert (libmooreb_hash_verify_NOLOCK(hash)); + return hash; + } + free(hash); + } + + return NULL; +} + +/* + * Free every node in the hash using libmooreb_hnode_free and + * cause the hash to become empty. + */ + +static void libmooreb_hash_free_nodes_NOLOCK(libmooreb_hash_t *hash) +{ + libmooreb_hscan_t hs; + libmooreb_hnode_t *node; + libmooreb_hash_scan_begin_NOLOCK(&hs, hash); + while ((node = libmooreb_hash_scan_next_NOLOCK(&hs))) { + libmooreb_hash_scan_delete_NOLOCK(hash, node); + libmooreb_hnode_free(hash, node); + } + hash->hash_nodecount = 0; + clear_table(hash); +} + +/* + * Free a dynamic hash table structure. + */ + +static void libmooreb_hash_destroy_NOLOCK(libmooreb_hash_t *hash) +{ + assert (libmooreb_hash_val_t_bit != 0); + assert (libmooreb_hash_isempty_NOLOCK(hash)); + free(hash->hash_table); + free(hash); +} + +/* + * Reset the hash scanner so that the next element retrieved by + * hash_scan_next() shall be the first element on the first non-empty chain. + * Notes: + * 1. Locate the first non empty chain. + * 2. If an empty chain is found, remember which one it is and set the next + * pointer to refer to its first element. + * 3. Otherwise if a chain is not found, set the next pointer to NULL + * so that hash_scan_next() shall indicate failure. + */ + +static void libmooreb_hash_scan_begin_NOLOCK(libmooreb_hscan_t *scan, libmooreb_hash_t *hash) +{ + libmooreb_hash_val_t nchains = hash->hash_nchains; + libmooreb_hash_val_t chain; + + scan->hash_table = hash; + + /* 1 */ + + for (chain = 0; chain < nchains && hash->hash_table[chain] == 0; chain++) + ; + + if (chain < nchains) { /* 2 */ + scan->hash_chain = chain; + scan->hash_next = hash->hash_table[chain]; + } else { /* 3 */ + scan->hash_next = NULL; + } +} + +/* + * Retrieve the next node from the hash table, and update the pointer + * for the next invocation of hash_scan_next(). + * Notes: + * 1. Remember the next pointer in a temporary value so that it can be + * returned. + * 2. This assertion essentially checks whether the module has been properly + * initialized. The first point of interaction with the module should be + * either hash_create() or hash_init(), both of which set hash_val_t_bit to + * a non zero value. + * 3. If the next pointer we are returning is not NULL, then the user is + * allowed to call hash_scan_next() again. We prepare the new next pointer + * for that call right now. That way the user is allowed to delete the node + * we are about to return, since we will no longer be needing it to locate + * the next node. + * 4. If there is a next node in the chain (next->hash_next), then that becomes the + * new next node, otherwise ... + * 5. We have exhausted the current chain, and must locate the next subsequent + * non-empty chain in the table. + * 6. If a non-empty chain is found, the first element of that chain becomes + * the new next node. Otherwise there is no new next node and we set the + * pointer to NULL so that the next time hash_scan_next() is called, a null + * pointer shall be immediately returned. + */ + + +static libmooreb_hnode_t *libmooreb_hash_scan_next_NOLOCK(libmooreb_hscan_t *scan) +{ + libmooreb_hnode_t *next = scan->hash_next; /* 1 */ + libmooreb_hash_t *hash = scan->hash_table; + libmooreb_hash_val_t chain = scan->hash_chain + 1; + libmooreb_hash_val_t nchains = hash->hash_nchains; + + assert (libmooreb_hash_val_t_bit != 0); /* 2 */ + + if (next) { /* 3 */ + if (next->hash_next) { /* 4 */ + scan->hash_next = next->hash_next; + } else { + while (chain < nchains && hash->hash_table[chain] == 0) /* 5 */ + chain++; + if (chain < nchains) { /* 6 */ + scan->hash_chain = chain; + scan->hash_next = hash->hash_table[chain]; + } else { + scan->hash_next = NULL; + } + } + } + return next; +} + +static void libmooreb_hash_insert_NOLOCK(libmooreb_hash_t *hash, libmooreb_hnode_t *node, const void *key) +{ + libmooreb_hash_val_t hkey, chain; + + assert (libmooreb_hash_val_t_bit != 0); + assert (node->hash_next == NULL); + assert (hash->hash_nodecount < hash->hash_maxcount); /* 1 */ + assert (libmooreb_hash_lookup_NOLOCK(hash, key) == NULL); /* 2 */ + + if (hash->hash_dynamic && hash->hash_nodecount >= hash->hash_highmark) /* 3 */ + grow_table(hash); + + hkey = hash->hash_function(key); + chain = hkey & hash->hash_mask; /* 4 */ + + node->hash_key = key; + node->hash_hkey = hkey; + node->hash_next = hash->hash_table[chain]; + hash->hash_table[chain] = node; + hash->hash_nodecount++; + + assert (libmooreb_hash_verify_NOLOCK(hash)); +} + +/* + * Find a node in the hash table and return a pointer to it. + * Notes: + * 1. We hash the key and keep the entire hash value. As an optimization, when + * we descend down the chain, we can compare hash values first and only if + * hash values match do we perform a full key comparison. + * 2. To locate the chain from among 2^N chains, we look at the lower N bits of + * the hash value by anding them with the current mask. + * 3. Looping through the chain, we compare the stored hash value inside each + * node against our computed hash. If they match, then we do a full + * comparison between the unhashed keys. If these match, we have located the + * entry. + */ + +static libmooreb_hnode_t *libmooreb_hash_lookup_NOLOCK(libmooreb_hash_t *hash, const void *key) +{ + libmooreb_hash_val_t hkey, chain; + libmooreb_hnode_t *nptr; + + hkey = hash->hash_function(key); /* 1 */ + chain = hkey & hash->hash_mask; /* 2 */ + + for (nptr = hash->hash_table[chain]; nptr; nptr = nptr->hash_next) { /* 3 */ + if (nptr->hash_hkey == hkey && hash->hash_compare(nptr->hash_key, key) == 0) + return nptr; + } + + return NULL; +} + +static int libmooreb_hash_alloc_insert_NOLOCK(libmooreb_hash_t *hash, const void *key, void *data) +{ + libmooreb_hnode_t *node = libmooreb_hnode_alloc(); + + if (node) { + libmooreb_hnode_init_NOLOCK(node, data); + libmooreb_hash_insert_NOLOCK(hash, node, key); + return 1; + } + return 0; +} + +/* + * Exactly like hash_delete, except does not trigger table shrinkage. This is to be + * used from within a hash table scan operation. See notes for hash_delete. + */ + +static libmooreb_hnode_t *libmooreb_hash_scan_delete_NOLOCK(libmooreb_hash_t *hash, libmooreb_hnode_t *node) +{ + libmooreb_hash_val_t chain; + libmooreb_hnode_t *hptr; + + assert (libmooreb_hash_lookup_NOLOCK(hash, node->hash_key) == node); + assert (libmooreb_hash_val_t_bit != 0); + + chain = node->hash_hkey & hash->hash_mask; + hptr = hash->hash_table[chain]; + + if (hptr == node) { + hash->hash_table[chain] = node->hash_next; + } else { + while (hptr->hash_next != node) + hptr = hptr->hash_next; + hptr->hash_next = node->hash_next; + } + + hash->hash_nodecount--; + assert (libmooreb_hash_verify_NOLOCK(hash)); + node->hash_next = NULL; + + return node; +} + +/* + * Verify whether the given object is a valid hash table. This means + * Notes: + * 1. If the hash table is dynamic, verify whether the high and + * low expansion/shrinkage thresholds are powers of two. + * 2. Count all nodes in the table, and test each hash value + * to see whether it is correct for the node's chain. + */ + +static int libmooreb_hash_verify_NOLOCK(libmooreb_hash_t *hash) +{ + libmooreb_hashcount_t count = 0; + libmooreb_hash_val_t chain; + libmooreb_hnode_t *hptr; + + if (hash->hash_dynamic) { /* 1 */ + if (hash->hash_lowmark >= hash->hash_highmark) + return 0; + if (!is_power_of_two(hash->hash_highmark)) + return 0; + if (!is_power_of_two(hash->hash_lowmark)) + return 0; + } + + for (chain = 0; chain < hash->hash_nchains; chain++) { /* 2 */ + for (hptr = hash->hash_table[chain]; hptr != 0; hptr = hptr->hash_next) { + if ((hptr->hash_hkey & hash->hash_mask) != chain) + return 0; + count++; + } + } + + if (count != hash->hash_nodecount) + return 0; + + return 1; +} + +/* + * Test whether the hash table is empty and return 1 if this is true, + * 0 if it is false. + */ + + +static int libmooreb_hash_isempty_NOLOCK(libmooreb_hash_t *hash) +{ + return hash->hash_nodecount == 0; +} + +static libmooreb_hnode_t *libmooreb_hnode_alloc(void) +{ + return malloc(sizeof *libmooreb_hnode_alloc()); +} + +static void libmooreb_hnode_free(libmooreb_hash_t *hash, libmooreb_hnode_t *node) +{ + if(hash->val_free_fun) { + (hash->val_free_fun)(node->hash_data); + } + if(hash->key_free_fun) { + (hash->key_free_fun)((void *)node->hash_key); + } + free(node); +} + +/* + * Initialize a client-supplied node + */ + +static libmooreb_hnode_t *libmooreb_hnode_init_NOLOCK(libmooreb_hnode_t *hnode, void *data) +{ + hnode->hash_data = data; + hnode->hash_next = NULL; + return hnode; +} + +static void *libmooreb_hnode_get_NOLOCK(libmooreb_hnode_t *node) +{ + return node->hash_data; +} + + +static const void *libmooreb_hnode_getkey_NOLOCK(libmooreb_hnode_t *node) +{ + return node->hash_key; +} + + +static libmooreb_hash_val_t libmooreb_hash_fun_default(const void *key) +{ + static unsigned long randbox[] = { + 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, + 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, + 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, + 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, + }; + + const unsigned char *str = key; + libmooreb_hash_val_t acc = 0; + + while (*str) { + acc ^= randbox[(*str + acc) & 0xf]; + acc = (acc << 1) | (acc >> 31); + acc &= 0xffffffffU; + acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; + acc = (acc << 2) | (acc >> 30); + acc &= 0xffffffffU; + } + return acc; +} + +static int libmooreb_hash_comp_default(const void *key1, const void *key2) +{ + return strcmp(key1, key2); +} + +static void init_hash_lock(libmooreb_hash_t *hash, int implicit_lock) { +} + +static void acquire_hash_read_lock(libmooreb_hash_t *hash) { +} + +static void release_hash_read_lock(libmooreb_hash_t *hash) { +} + +static void acquire_hash_write_lock(libmooreb_hash_t *hash) { +} + +static void release_hash_write_lock(libmooreb_hash_t *hash) { +} + +static libmooreb_hash_t *libmooreb_hash_create(libmooreb_hashcount_t maxcount, libmooreb_hash_comp_t compfun, + libmooreb_hash_fun_t hashfun, int implicit_lock) +{ + libmooreb_hash_t *retval; + + retval = libmooreb_hash_create_NOLOCK(maxcount, compfun, hashfun); + init_hash_lock(retval, implicit_lock); + return(retval); +} + +static void libmooreb_hash_free_nodes(libmooreb_hash_t *hash) +{ + acquire_hash_write_lock(hash); + libmooreb_hash_free_nodes_NOLOCK(hash); + release_hash_write_lock(hash); +} + +static void libmooreb_hash_destroy(libmooreb_hash_t *hash) +{ + acquire_hash_write_lock(hash); + libmooreb_hash_destroy_NOLOCK(hash); + /* don't release the write lock; we've just free'd hash */ +} + +static void libmooreb_hash_scan_begin(libmooreb_hscan_t *scan, libmooreb_hash_t *hash) +{ + acquire_hash_read_lock(hash); + libmooreb_hash_scan_begin_NOLOCK(scan, hash); + release_hash_read_lock(hash); +} + +static libmooreb_hnode_t *libmooreb_hash_scan_next(libmooreb_hscan_t *scan) +{ + libmooreb_hnode_t *retval; + + retval = libmooreb_hash_scan_next_NOLOCK(scan); + return(retval); +} + +static libmooreb_hnode_t *libmooreb_hash_lookup(libmooreb_hash_t *hash, const void *key) +{ + libmooreb_hnode_t *retval; + + acquire_hash_read_lock(hash); + retval = libmooreb_hash_lookup_NOLOCK(hash, key); + release_hash_read_lock(hash); + return(retval); +} + +static int libmooreb_hash_alloc_insert(libmooreb_hash_t *hash, const void *key, void *data) +{ + int retval; + + acquire_hash_write_lock(hash); + retval = libmooreb_hash_alloc_insert_NOLOCK(hash, key, data); + release_hash_write_lock(hash); + return(retval); +} + +static void *libmooreb_hnode_get(libmooreb_hnode_t *node) +{ + void *retval; + + retval = libmooreb_hnode_get_NOLOCK(node); + return(retval); +} + +static const void *libmooreb_hnode_getkey(libmooreb_hnode_t *node) +{ + const void *retval; + + retval = libmooreb_hnode_getkey_NOLOCK(node); + return(retval); +} + +static void libmooreb_hash_set_val_free_func(libmooreb_hash_t *hash, libmooreb_hash_free_fun_t f) +{ + acquire_hash_write_lock(hash); + hash->val_free_fun = f; + release_hash_read_lock(hash); + return; +} + +#define needs_markup(c) (('&' == c) || ('\'' == c) || ('\"' == c) || ('>' == c) || ('<' == c) || (c >= 127) || (c < 32)) +#define entity_length(c) (((c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3) /* "&#" + (numbase10digits(c)) + ";" */ + +static libmooreb_hash_val_t int_hasher(const void *key) { + return((libmooreb_hash_val_t)key); +} + +static int int_hasher_comp(const void *key1, const void *key2) { + int val; + + if(key1 == key2) { + val = 0; + } + else if(key1 < key2) { + val = -1; + } + else { + val = 1; + } + return(val); +} + +static libmooreb_hash_t *get_pgsql_types(void) { + const char *query = "select oid,typname from pg_type"; + const char *db = "dbname = template1"; /* BUG: hardcoded db name */ + PGconn *connection = NULL; + libmooreb_hash_t *h = NULL; + + connection = PQconnectdb(db); + if (CONNECTION_OK == PQstatus(connection)) { + PGresult *result = NULL; + + result = PQexec(connection, query); + if (PGRES_TUPLES_OK == PQresultStatus(result)) { + int num_columns = -1; + int num_rows = -1; + + num_columns = PQnfields(result); + num_rows = PQntuples(result); + if((2 == num_columns) && (0 == PQfformat(result, 0)) && (0 == PQfformat(result, 1))) { + h = libmooreb_hash_create(LIBMOOREB_HASHCOUNT_T_MAX, int_hasher_comp, int_hasher, LIBMOOREB_HASH_NOLOCK); + if(h) { + int row = 0; + + libmooreb_hash_set_val_free_func(h, free); + + /* read in all of the types and put them in a hashtable */ + for(row=0; row\n", + num_rows, + num_cols); + if((num_chars_printed >= sizeof(temp_buf)) || + libmooreb_strbuf_concat(strbuf, temp_buf, num_chars_printed+1)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + + /* + * Now print the column descriptions + */ + { + int col; + + for(col=0; col\n", + col, + (char *)libmooreb_hnode_get(libmooreb_hash_lookup(oid_type_hash, (void *)type_oid)), + ((0 == format) ? "text" : ((1 == format) ? "binary" : "UNKNOWN")), + PQfname(result, col)); + if((num_chars_printed >= sizeof(temp_buf)) || + libmooreb_strbuf_concat(strbuf, temp_buf, num_chars_printed+1)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + } + + /* + * Now print the rows + */ + { + int col, row; + + for(row=0; row\n", + row); + if((num_chars_printed >= sizeof(temp_buf)) || + libmooreb_strbuf_concat(strbuf, temp_buf, num_chars_printed+1)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + + for(col=0; col", + col); + if((num_chars_printed >= sizeof(temp_buf)) || + libmooreb_strbuf_concat(strbuf, temp_buf, num_chars_printed+1)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + + /* now print the contents of each column */ + + + if(PQgetisnull(result, row, col)) { + if(libmooreb_strbuf_concat(strbuf, null_col_entity_ref, sizeof(null_col_entity_ref))) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + else if (raw_string) { + if(is_text) { + const char *p = raw_string; + int raw_length = 1; /* include the terminating null byte */ + int extra_entity_bytes = 0; + + while(*p) { + if(needs_markup(*p)) { + extra_entity_bytes += (entity_length(*p) - 1); + } + raw_length++; + p++; + } + + if(0 == extra_entity_bytes) { + /* no need for escape */ + if(libmooreb_strbuf_concat(strbuf, raw_string, raw_length)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + else if((extra_entity_bytes < (sizeof(cdata_start)+sizeof(cdata_end)-2)) || + strstr(raw_string, cdata_end)) { + /* just escape with numerical entity tags */ + char *escaped; + + escaped = (char *)malloc((raw_length+extra_entity_bytes+1)*sizeof(char)); + if(escaped) { + int c; + int i=0; + + p = raw_string; + while((c = *p++)) { + if(needs_markup(c)) { + escaped[i++] = '&'; + escaped[i++] = '#'; + if(c >= 100) { + escaped[i++] = c/100 + '0'; + c = c % 100; + escaped[i++] = c/10 + '0'; + c = c % 10; + escaped[i++] = c + '0'; + } + else if(c >= 10) { + escaped[i++] = c/10 + '0'; + c = c % 10; + escaped[i++] = c + '0'; + } + else { + escaped[i++] = c + '0'; + } + escaped[i++] = ';'; + } + else { + escaped[i++] = c; + } + } + escaped[i++] = '\0'; + if(libmooreb_strbuf_concat(strbuf, escaped, i)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + free(escaped); + } + else { + /* cannot allocate place to entity escape raw string. bail */ + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + else { /* enough bytes to mandate a CDATA section */ + if(libmooreb_strbuf_concat(strbuf, cdata_start, sizeof(cdata_start))) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + if(libmooreb_strbuf_concat(strbuf, raw_string, raw_length)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + if(libmooreb_strbuf_concat(strbuf, cdata_end, sizeof(cdata_end))) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + } + else if(is_binary) { + int len, i; + char out[2]; + + out[1] = '\0'; + len = PQgetlength(result, row, col); + for(i=0; i<2*len; i++) { + char nibble; + + if(0 == (i%2)) { + /* high nibble */ + nibble = (raw_string[i/2] & 0xf0); + } + else { + /* low nibble */ + nibble = (raw_string[i/2] & 0x0f); + } + + if(0 <= nibble <= 9) { + out[0] = '0' + nibble; + } + else if(10 <= nibble <= 15) { + out[0] = 'a' + nibble - 10; + } + else { + /* shouldn't get here */ + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + if(libmooreb_strbuf_concat(strbuf, out, 2)) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + } + else { + /* not of any known formats: bail. */ + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + else { + /* the string from PQgetvalue is NULL and it shouldn't be. bail. */ + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + + /* print the closing column tag */ + if(libmooreb_strbuf_concat(strbuf, close_col, sizeof(close_col))) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + + if(libmooreb_strbuf_concat(strbuf, close_row, sizeof(close_row))) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + } + } + if(libmooreb_strbuf_concat(strbuf, close_pgresult, sizeof(close_pgresult))) { + libmooreb_strbuf_destroy(strbuf); + return(NULL); + } + retval = libmooreb_strbuf_finish(strbuf); + } + else { + /* could not allocate strbuf */ + } + return(retval); +} + +static int test_PGresult_as_xml(void) { + const char *query = "select oid,typname,typlen,typtype from pg_Type where oid<20"; + const char *db = "dbname = template1"; /* BUG: hardcoded db name */ + PGconn *connection = NULL; + + connection = PQconnectdb(db); + if (CONNECTION_OK == PQstatus(connection)) { + PGresult *result = NULL; + + result = PQexec(connection, query); + if (PGRES_TUPLES_OK == PQresultStatus(result)) { + const char *s; + + s = PGresult_as_xml(result, 1); + printf("%s", s); + free((char *)s); + } + PQclear(result); + } + PQfinish(connection); + return(0); +} + +static int test_get_pgsql_types(void) { + libmooreb_hash_t *h = NULL; + libmooreb_hscan_t *s; + libmooreb_hnode_t *n; + + h = get_pgsql_types(); + s = libmooreb_hscan_create(); + + if(h && s) { + libmooreb_hash_scan_begin(s, h); + while((n = libmooreb_hash_scan_next(s))) { + const char *type_name; + int oid; + + oid = (int)libmooreb_hnode_getkey(n); + type_name = (const char *)libmooreb_hnode_get(n); + fprintf(stdout, "%d\t%s\n", oid, type_name); + } + libmooreb_hscan_destroy(s); + libmooreb_hash_free_nodes(h); + libmooreb_hash_destroy(h); + } + return(0); +} + +int main(void) { + test_get_pgsql_types(); + test_PGresult_as_xml(); + + return(0); +} diff -Naur postgresql-7.4.1.distrib/src/interfaces/libpq/libpq-fe.h postgresql-7.4.1.patched/src/interfaces/libpq/libpq-fe.h --- postgresql-7.4.1.distrib/src/interfaces/libpq/libpq-fe.h Tue Aug 26 17:33:34 2003 +++ postgresql-7.4.1.patched/src/interfaces/libpq/libpq-fe.h Mon Jan 26 20:13:54 2004 @@ -408,6 +408,12 @@ const PGresult *res, const PQprintOpt *ps); /* option structure */ +/* === in fe-printxml.c === */ + +extern const char * +PGresult_as_xml(const PGresult *result, int include_dtd); + + /* * really old printing routines */