1
0
Fork 0
mirror of https://github.com/KingDuckZ/dindexer.git synced 2025-07-02 14:04:22 +00:00
This commit is contained in:
King_DuckZ 2015-12-26 22:16:51 +00:00
parent c409a3e0a2
commit d8f3578497
75 changed files with 58611 additions and 1 deletions

View file

@ -4,6 +4,7 @@ project("${bare_name}-if" VERSION 0.1.3 LANGUAGES CXX C)
list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
include(GetGitRevisionDescription)
include(ExternalProject)
option(DINDEXER_DEBUG_CFG_FILE "Enable to set the config file path to the build path" OFF)
option(DINDEXER_WITH_MEDIA_AUTODETECT "Enable code that tries to autodetect the media type and sets --type automatically" ON)
@ -24,7 +25,7 @@ endif()
message(STATUS "Config file set to \"${DINDEXER_CONFIG_FILE}\"")
find_package(Boost 1.53.0 REQUIRED COMPONENTS program_options)
find_package(PostgreSQL REQUIRED)
find_package(PostgreSQL 8.3 REQUIRED)
find_package(YamlCpp 0.5.1 REQUIRED)
add_library(${PROJECT_NAME} INTERFACE)
@ -47,6 +48,25 @@ target_include_directories(${bare_name}-inc
INTERFACE ${CMAKE_SOURCE_DIR}/include
)
string(REPLACE ";" " -I" pqtypes_inc_dirs "-I${PostgreSQL_INCLUDE_DIRS}")
set(pqtypes_name "libpqtypes-1.5.1")
set(pqtypes_prefix ${CMAKE_CURRENT_BINARY_DIR}/lib/${pqtypes_name})
ExternalProject_Add(pqtypes_working
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/${pqtypes_name}
PREFIX lib/${pqtypes_name}
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lib/${pqtypes_name}/configure --prefix ${pqtypes_prefix} "CPPFLAGS=${pqtypes_inc_dirs}" --quiet
BUILD_COMMAND ${MAKE}
BUILD_IN_SOURCE 0
INSTALL_DIR ${pqtypes_prefix}
)
add_library(pqtypes SHARED IMPORTED)
set_target_properties(pqtypes PROPERTIES
IMPORTED_LOCATION ${pqtypes_prefix}/lib/libpqtypes.so
IMPORTED_LINK_INTERFACE_LANGUAGES C
#INCLUDE_DIRECTORIES ${pqtypes_prefix}/include
)
add_subdirectory(src/scan)
add_subdirectory(src/pq)
add_subdirectory(src/main)

View file

@ -0,0 +1,32 @@
LIBPQTYPES LIBRARY
------------------
Distributed by:
eSilo, LLC. (see LICENSE file)
Maintainers:
People who currently fix bugs and perform most of the forward development.
Andrew Chernow ac (at) esilo (dot) com
Written by:
Original people behind the libpqtypes project core functionality and
architecture (June 2007 - Apr 2008). The wounded warriors who battled the
PostgreSQL team trying to get libpqtypes into the main source tree. That
battle continues to be waged as we believe libpqtypes is a far more
advanced and powerful client interface for PostgreSQL than the vanilla libpq.
Andrew Chernow ac (at) esilo (dot) com
Merlin Moncure mmoncure (at) gmail (dot) com
Contributors:
People who have proven to be helpful over the years with their critical
thinking, design ideas and bug detection.
Jeremy Smith jeremy (at) esilo (dot) com
> Last updated 2011-04-10

View file

@ -0,0 +1,335 @@
LIBPQTYPES CHANGE LOG
---------------------
Initially, libpqtypes was a libpq patch named (PQparam/PGparam) and can
be found on the -hackers and -patches mailing lists. This change log
begins at the point libpqtypes was put on pgfoundry as its own project.
To gain access to the "(NOT RELEASED)" code, do a cvs checkout.
Jun 27, 2013 v1.5.1
- fixed issue in spec.c where the type name was not being lower cased
when it was required.
- Moved AUTHORS, ChangeLog, INSTALL, LICENSE and README to EXTRA_DIST
- Jozef Mlich
- Modified configure.ac and Makefile.am to properly do SO versioning.
- Jozef Mlich
- error.c uses the GCC contructor attribute so it can create the the
TLS key for each thread's error structure. For whatever reason,
calling the constructor function 'my_init' was causing it to never
get called?!? Renamed it and all is well. This becomes apparent
when libpqtypes is linked with apps using pthread_key_create.
- Removed -no-undefined from LDFLAGS in Makefile.am. This proved to be
annoying.
- Updated groff2html so it no longer includes an inline style for
pre tag width.
- Updated pqt-handlers.3, the PGtypeHandler structure was out of date.
- pqt-composites.3 man page had a bug when registering the simple and complex
composite. It was only indicating 1 type instead of 2.
Apr 11, 2011 v1.5.0
- Bumped version to 1.5.0 due to many API removals and additions.
- Added -avoid-version and -no-undefined to Makefile.am.
- Added PQinitTypes to replace PQtypesRegister. The new function
adds no new behavior, just a name change.
- Deprecated PQtypesRegister, use PQinitTypes in its place.
- PQclearTypes was added to allow one to clear all registered type handlers.
- Added an internal API pqt_cleartypes since clearing the type data
is now called from PQclearTypes and pqt_eventproc.
- Added a PQclearSpecs function that allows clearing all prepared specifiers
in a single call. Currently, specs can be cleared one at a time by
setting PQspecPrepare's 3rd 'format' argument to NULL. This is useful
but makes clearing all specs difficult because it is not always known
what specs have been prepared by all modules of an application.
- Removed "automatic" re-registering of types during PQresetXXX. This
fails miserably during asynchronous resets. Replaced with the
folowing call sequence: PQresetXXX, PQclearTypes, PQregisterTypes.
NOTE: It is not required to re-register type handlers after a PQresetXXX.
It is only useful if there is concern that registered types may have
gone stale.
WARNING: If an application is reliant upon automatic re-registration of
type handlers during a PQresetXXX, you will have to update your
code to manually do a PQclearTypes followed by PQregisterTypes just
after the reset call.
- Removed the orig_xxx members and regtype from PGtypeHandler structure.
These members were used by the reset event which was yanked.
- Finally 86'd PQregisterTypeHandler, which has been deprecated since
v1.4.0, around 18 months as of Apr 2011.
- Updated copyright date from 2009 to 2011, affected source code and
man pages.
- Updated AUTHORS file to include a list of maintainers and some verbage.
- Deprecated PQregisterSubClasses, PQregisterComposites and
PQregisterUserDefinedTypes in favor of PQregisterTypes. This new
function takes a 'which' argument that indicates if PQT_SUBCLASS,
PQT_COMPOSITE or PQT_USERDEFINED types are being registered. This
function provides full support for asynchronous type registration via
a simple 'async' boolean argument. The typical PQconsumeInput, PQisBusy
and PQgetResult should be used to obtain a result. After a result is
obtained, the new PQregisterResult function needs to be called
to complete the registration.
- Added PQregisterResult which registers the types found within
the result set. Added to support asynchronous type registration and
to avoid type lookups for every new connection by caching the PGresult
type data for use with this function.
- Add PQregisterTypes.3 man page and added it to the make system install.
- Add PQregisterResult.3 man page and added it to the make system install.
- Removed referneces in documentation and code examples to PQregisterSubClasses,
PQregisterComposites and PQregisterUserDefinedTypes. Replaced with
new PQregisterTypes call.
- Regression test now uses PQregisterTypes. NOTE: no regression test
for PQregisterResult or asynchronous type registration yet.
- Marked PQregisterSubClasses.3, PQregisterComposites.3 and
PQregisterUserDefinedTypes.3 man pages as deprecated. They are still
installed with a `make install` and will be until removed.
- Added PQparamDup, which given a PGparam will make an exact duplicate.
This is useful in cases where you want to queue qeuries to execute
at a later time, like a connection pooler. The problem is PGparamCreate
requires a connection object, which may not be available when attempting
to enqueue a query with its PGparam object. Instead, a PGparam object
can used for the sole purpose of creating duplicates while there are
no available PGconn objects.
- Updated PGparamCreate man page to include documentation about PGparamDup.
Feb 26, 2010 v1.4.3
- Fixed bug in handler.c. The type lookup query was matching typename
its array based on the array name being '_' plus the typename. This
was slower and unsafe.
Oct 03, 2010 v1.4.2
- Fixed bug in events.c. When clearing a type handler list the 'max' count
(amount allocated) was not set to zero. This caused a crash during
a PQreset call.
- Updated PQgetf documentation, clarified how to handle errors when
getting data types that require memory management: PGresult and PGarray.
- Moved libpq-events.h out of libpqtypes.h and into libpqtypes-int.h.
Added libpq-fe to libpqtypes.h.
Apr 06, 2010 v1.4.1
- Fix memory leak in events.c during PGEVT_CONNRESET. Not a big deal as the
leak occurs within an out-of-memory error case, so the process is most
likely toast. Either way, leak has been plugged.
- pqt_allowsptr was returning FALSE if the type's schema was not provided.
It now returns TRUE as it should.
- Fixed how missing types are handled during register functions. They were
not properly indicating errors in all cases.
Nov 10, 2009 v1.4.0
- Added PQgetvf to compliment PQgetf. PQgetf is now a light wrapper
PQgetvf. This did not change the behavior of PQgetf in any way.
- Fixed bug in IPv6 regression_test. When AF_INET6 is available, the
'hints' parameter to getaddrinfo should be supplied with the ai_family
set to AF_INET6.
- 63% Performance improvement to the type handling duplication which occurs
whenever a result is copied or a PGparam is created. Previously, the
type handlers contained allocated strings for the identifier names:
like type and schema name. When duplicating the handlers, the strdup
cost added up to a noticeable amount of time (mostly seen during
PQgetf(a_composite_or_array) which duplicates the handlers when generating
a new PGresult). These strings were changed to fixed length buffers
65 bytes wide; 64 max char identifier plus terminating NUL. The names
no longer need to be deep copied, they are instead copied as part of a
larger memcpy. Additionally, the attDescs array now uses a fixed length
buffer when the number of compsoite attrs is <= 16 (common case). When
larger, allocation is used. 'freeAttDescs' flag was added to the
PGtypeHandler structure to indicate if the attDescs requires freeing.
In total, these changes made type handling duplication 63% faster.
NOTE:
This increased the size of PGtypeHandler by around 434 bytes. The
size of PGrecordAttDesc increased by 65 bytes. This change modified
2 public structures so please ensure your libpqtypes.h is current.
- Split PQregisterTypeHandler into three different functions:
PQregisterComposites, PQregisterUserDefinedTypes and PQregisterSubClasses.
This was done for two reasons: needed the ability to register multiple
types at the same time, avoid round trip issues, and simplify the interface.
- PQregisterTypeHandler is now deprecated and should no longer be used. It
exists only for compatibility reasons. It is a wrapper to
PQregisterComposites, PQregisterUserDefinedTypes and PQregisterSubClasses.
- The PGEVT_REGISTER event was not be handled properly by libpqtypes. It
neglected to initialize the internal type formatting information.
- Error case in pqt_parse() was accessing a NULL pointer.
- Bug in network.c when getting text inet/cidr types. Apparently, windows
requires a few hints about the address family when using getaddrinfo:
both IPv4 and IPv6. This must be a recent Microsoft winsock update as
this code used to work.
Sep 15, 2009 v1.3.4
- In v1.2b, a caching system for type spec format strings was introduced.
PQputf and PQgetf would cache the last spec format seen, like "%int4".
This avoided constant type handling lookups when using arrays, since
one must put/get every element. This version expands on this concept
by adding an additional API call: PQspecPrepare. The application can
now prepare an unlimited number of spec format strings.
NOTE:
This change does not effect the behavior of putf or getf in older versions.
Although, some libpqtypes code may run slower since type spec format
strings are no longer automatically cached. If you have loops calling
putf or getf with the same spec format string, typically arrays, you
would probably benefit from PQspecPrepare.
- Added a PQsendf() and PQsendvf(), same as PQexecf and PQexecvf except they
are asynchronous versions. Created manual pages for these functions as well.
- Fixed a bug in PQexecf and PQexevf documentation, the prototypes were
documented as returning an int rather than a PGresult*.
- Fixed spelling error in PQspecPrepare.3 man page and added PQsendf and
PQsendvf to the list of supported functions.
- Fixed memory leak in the error handling code. Multi-threaded windows
builds were not cleaning up the internal error structure in error.c.
This is because windows has no automatic cleanup for TLS storage. The
only solution was to use fixed length buffers and truncate unusually
long error messages; including error fields.
May 29, 2009 v1.3.3
- Added PQexecf and PQexecvf which allows one to pack parameters and
execute a command all in one statement. Example:
PQexecf(conn, "SELECT %int4 + %int4", 1, 1);
- Updated the PQputf.3 man page execf documentation which stated that
execf should be implemented by the application ... not libpqtypes.
Apparently, we no longer feel that way :)
- Fixed bug in datetime.c that accessed index 7 of a 7 byte array, ooppps.
Thanks to GCC version 4.3.2 for catching this one:
"src/datetime.c:930: warning: array subscript is above array bounds"
Apr 27, 2009 v1.3.2
- Slight change in the behavior of PQputf and PQputvf. These functions
will now silently auto-fill unset composite fields with NULLs. This only
pertains to the last N fields: if there are 10 fields in a compsoite
and only 7 are put, the last 3 fields will be auto-filled with SQL NULLs.
Previously, an error was raised indicating the client and server have
different ideas about the number of fields in the composite. That error
is still raised if the number of params exceeds the number of composite
fields. Auto-filling only occurs when the number of params falls short.
- User-defined CFLAGS were being lost due to a dumb assigment at the
top of the script. It now properly appends flags to the ones supplied
by the user.
- Updated PQgetf man page to include a warning about getting arrays and
composites. If getf fails after an array or composite was retrieved,
the PGresult representing the array/composite must still be cleared.
The solution is to get arrays/composites by themselves or to always
make them the last field in the getf call (the latter only works
if there is only one array or composite in the field list).
- Fixed SEGFAULT when getting a NULL compsoite array item.
Feb 11, 2009 v1.3.1
- PQgetf of an empty array was always returning a result with a single
tuple (array item), eventhough the PGarray members were correctly set
for an empty array. This has been fixed.
- Added empty array test to regression-test.c.
- getaddrinfo is missing on Windows 2000 and earlier versions. Wspiapi.h
and ws2tcpip.h must be included to provide a replacement. libpqtypes-int.h
neglected to include Wspiapi.h, which it now does.
Microsoft docs on issue: http://support.microsoft.com/kb/955045
Feb 03, 2009 v1.3.0
- The internal put_int2 and put_int4 type handlers were passing a va_arg
call as an argument to the pqt_buf_putint2 and pqt_buf_putint4 macros.
This corrupted the value being put on UnixWare 7.1.4. The return
of va_arg is now stored in a local variable that is passed to
one of the putintX macros.
- Renamed PGinet sa_len member since it conflicts with some systems.
For instance: SCO UnixWare defines a sa_len macro in sys/socket.h
#define sa_len sa_un.sa_s.sa_saus_len
Renamed it to sa_buf_len and changed all references.
- network.c had an issue with struct sockaddr not being zero'd on AIX.
This caused all inet/cidr tests to fail. Update regresion-test.c
with similar fixes.
- AI_NUMERICHOST defined if not available in network.c. If getaddrinfo
fails with EAI_BADFLAGS, its tried again w/o numeric host.
- AIX defaults to an unsigned char, which broke "char" type handling.
Changed PGchar typedef to signed char and added the GCC option
-fsigned-char to configure.ac.
- Changed the return type of PQgeterror from a const char* to a char*.
Having it be const served no purpose.
- Added PQparamCount which returns the number of parameters in a PGparam.
Added PQparamCount.3 man page and updated Makefile.
- Made errFields implementation more memory efficient by allocating all
fields as a single reusable buffer.
- Reformatted code to match PostgreSQL Project. This changed every
source file so take note when looking at CVS diffs.
- Defined HAVE_VSNPRINTF in port.c when compiling for mingw or cygwin.
- Fixed man page errors in PQputf and pqt-handlers.
- Updated INSTALL file to include the 'install' and uninstall' targets.
- Makefile.am (unix make) now installs PQgetErrorField man page.
- Update copyright notices to reflect 2009: source files, LICENSE
and man pages.
- Removed typeargs state flag PUT_STATE_FREEOUT from param.c since it
is no longer used.
Dec 03, 2008 v1.2e
- Added support for PQresultErrorField as PQgetErrorField. PQparamExec and
PQparamExecPrepared clear resutls in a failure status, thus removing
the abillity to get error fields. PQgetErrorField can now be used and
it doesn't require a result.
Dec 02, 2008 v1.2d
- Fixed bug when putting a NULL array item; %null or %pqt.null. array.c
was not adding in the 4 byte length when the length's value was NULL_LEN.
This resulted in "ERROR: insufficient data left in message" errors from
the server during a PQparamExec() call.
- Added a %null test in regression-test.c for composites and arrays.
Nov 18, 2008 v1.2c
- removed GCC __thread modifier from error.c. Only pthread TLS keys
are now used. The TLS key for the error system is now intialized
with the GCC function attribute "constructor"; which replaced _init().
This GCC attribute has been around since GCC version 2.7.0, old enough.
The error system used to intialize the TLS key via pthread_once(),
but this function is broken on all version of Solaris prior to 10
(its actually a stub function the returns 0). This means libpqtypes
versions prior to this one cannot use thread-safe mode on Solaris 9 or
below. Windows continues to use __declspec(thread).
- The PATH geo type was completely broken (SIGBUS) on many RISC processors,
we tested a couple of UltraSparcs, Itanium, PA-RISC and MIPS. This is
due to memory alignment issues (boy, spoiled by x86!). A configure
check was added to detect when strict memory alignment is required
(STRICT_MEMORY_ALIGNMENT) and the source is toggled appropriately.
- Add pqt_buf_xxx functions to abstract reading and writing data
to/from buffers. Another memory alignment change.
- fixed regression-test.c on hpux which was referencing unknown macros:
LLONG_MAX and LLONG_MIN. HPUX uses LONG_LONG_MAX.
- cleaned up a few things in Makefile.am (unix make file)
- win32.mak for MSVC had an issue with include variable. created INC2
to solve this.
Nov 11, 2008 v1.2b
- Added a type specifier caching system. Whenever putf or getf are
called, it compares the specifier string with the last one libpqtypes
saw. If it is a match, the parsing and type handler lookup
stage can be bipassed, giving a 25% performance increase for arrays
and large result sets. This also makes libpqtypes noticably faster
than using libpq in text mode ... PQexec.
- preprocessing bug in port.c, #elif HAVE_VSNPRINTF instead of
#elif defined(HAVE_VSNPRINTF).
- windows was completely broken from a change in v1.2a. Apparantley
windows has different memory addresses for DLL exported functions;
the address seen outside the DLL and the address seen from within the DLL.
This broke the PQtypesRegister macro that was referencing PQtypesEventProc,
since the external address was being used to register the event proc
but the internal address was being used by libpqtypes. This problem was
solved by converting PQtypesRegister to a function, which forces the
event proc to always be referenced from within libpqtypes.
PQtypesEventProc was completely removed form the public interface.
- Updated make system to be more configurable, allows setting things
like CC, CFLAGS, etc.. at the prompt (see INSTALL).
Sept 26, 2008 v1.2a
- libpqtypes uses the libpq event system, an approaved patch for 8.4.
The object hooks patch was redesigned and than renamed to libpq events.
- bug fix in timestamp code
- memory leak fix in error system, didn't clean up thread memory.
- pqytpe's PGEventProc is now a public function named PQtypesEventProc
May 19, 2008 v1.0c
- Bug fix in datetime.c. When performing a time value conversion, 1-based
and 0-based month values were being used/confused.
- libpqtypes using objecthooks, a proposed patch for libpq allowing
outside apps, like libpqtypes, to hook into libpq.
May 09, 2008 v1.0b
- Added copyright notice to all source files.
- Updated several man pages.
- Added an include for stdarg.h to libpqtypes.h
- Updated makefile and groff2html
April 28, 2008 v1.0
- libpqtypes project was created on pgfoundry
- make system was put into place (mingw, cygwin and msvc support)

View file

@ -0,0 +1,119 @@
LIBPQTYPES INSTALLTION GUIDE
----------------------------
*) UNIX BUILDS
For quick builds, run the below:
]# ./configure
]# make
]# make install
(Also `make uninstall', which removes all installed headers,
libs and mans pages)
By default, only a shared library is built. To build a static
library, use the `--enable-static' option.
To build a thread-safe version, use the `--enable-thread-safety' option.
To use libpqtypes in a thread-safe environment, the system must have
pthreads installed. Most systems will have /usr/include/pthread.h
and /usr/lib/libpthread.so or /usr/lib/libthread.so.
The default prefix is `/usr/local', so if the `--prefix' option is
not specified the header file(s) will be installed at
/usr/local/include and the library file(s) will be installed at
/usr/local/lib.
If your system doesn't include a `long long' data type, resulting in
a library compile error, you can specify an alternative by setting
PQT_LONG_LONG=data_type during configure.
]# CFLAGS="-DPQT_LONG_LONG=my_int8" ./configure
Files installed:
- $(prefix)/include/libpqtypes.h
- $(prefix)/lib/libpqtypes.so
When `--enable-static' is specified:
- $(prefix)/lib/libpqtypes.a
To dynamically link with libpqtypes, pass -lpqtypes to the linker.
*) MINGW AND CYGWIN BUILDS
There is no configure for MinGW or Cygwin. Execute the below from
the root of the source tar ball within the cygwin shell or msys.
]# make -f Makefile.win32 [options] [targets]
*) MSVC BUILDS
MSVC versions 6, 7 and 8 have all been tested. Versions prior to 6
may work but are not supported.
The MSVC build uses `nmake' and can be executed from the root of the
source tar ball at a DOS prompt:
> nmake -f win32.mak [options] [targets]
*) MINGW, CYGWIN & MSVC BUILDS:
For the below examples, MAKE is defined as:
# MSVC
MAKE = nmake -f win32.mak
# CYGWIN & MINGW
MAKE = make -f Makefile.win32
Targets:
all - build the libpqtypes library
test - build the libpqtypes regression test
clean - delete all files generated by compiles
install - Install the binaries, headers and man3 pages
uninstall - Uninstalls libpqtypes, reverse of install target
Thread-safe:
$(MAKE) MT=1
NOTE: MinGW and/or Cygwin may require installing pthreads.
Specify alternative 64-bit int data type:
$(MAKE) PQT_LONG_LONG=my_int8
Global make & nmake compiler/linker variables:
CC = C compiler
INC = includes
CFLAGS = compiler flags
LPATH = library path. MINGW and CYGWIN use -L while MSVC can supply a single path
# If libpq is not installed and you use MSVC, try this (replace paths):
> $(MAKE) INC="-Ic:\pgsql\src\interfaces\libpq" LPATH="-Ic:\pgsql\src\interfaces\libpq\Release"
Files generated:
libpqtypes.dll - Windows DLL
libpqtypesdll.lib - Import lib for DLL
libpqtypes.lib - Static library
Where to install files:
- System Include Path: libpqtypes.h
- System Library Path: libpqtypes.dll
- System Library Path: libpqtypesdll.lib
- System Library Path: libpqtypes.lib (static lib)

View file

@ -0,0 +1,22 @@
LIBPQTYPES
Software extension to the PostgreSQL libpq interface.
Copyright (c) 2009 eSilo, LLC. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement
is hereby granted, provided that the above copyright notice and this
paragraph and the following two paragraphs appear in all copies.
IN NO EVENT SHALL ESILO, LLC. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
ESILO, LLC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ESILO, LLC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
AND ESILO, LLC. HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.

View file

@ -0,0 +1,68 @@
AM_LDFLAGS =
AUTOMAKE_OPTIONS = foreign
@SET_MAKE@
srcfiles = src/array.c src/datetime.c src/error.c \
src/events.c src/exec.c src/geo.c src/handler.c \
src/misc.c src/network.c src/numerics.c src/param.c \
src/port.c src/record.c src/spec.c src/utils.c src/varlena.c \
src/libpqtypes.h src/libpqtypes-int.h
manpages = docs/man3/PQgeterror.3 docs/man3/PQgetf.3 \
docs/man3/PQlocalTZInfo.3 docs/man3/PQparamClear.3 \
docs/man3/PQparamCreate.3 docs/man3/PQparamExec.3 \
docs/man3/PQparamExecPrepared.3 docs/man3/PQparamReset.3 \
docs/man3/PQparamSendQuery.3 docs/man3/PQparamSendQueryPrepared.3 \
docs/man3/PQputf.3 docs/man3/PQputvf.3 \
docs/man3/PQseterror.3 docs/man3/pqt-composites.3 docs/man3/pqt-handlers.3 \
docs/man3/PQgetErrorField.3 docs/man3/pqt-specs.3 \
docs/man3/PQtypesRegister.3 docs/man3/PQparamCount.3 \
docs/man3/PQexecf.3 docs/man3/PQexecvf.3 docs/man3/PQspecPrepare.3 \
docs/man3/PQsendf.3 docs/man3/PQsendvf.3 docs/man3/PQgetvf.3 \
docs/man3/PQregisterComposites.3 docs/man3/PQregisterUserDefinedTypes.3 \
docs/man3/PQregisterSubClasses.3 docs/man3/PQregisterTypes.3 \
docs/man3/PQregisterResult.3 docs/man3/PQinitTypes.3 \
docs/man3/PQclearTypes.3 docs/man3/PQclearSpecs.3
lib_LTLIBRARIES = libpqtypes.la
libpqtypes_la_SOURCES = $(srcfiles)
libpqtypes_la_LDFLAGS = -version-info $(LIBPQTYPES_SO_VERSION)
include_HEADERS = src/libpqtypes.h
dist_man_MANS = $(manpages)
noinst = src/libpqtypes-int.h src/getaddrinfo.h
EXTRA_DIST = install-sh src/regression-test.c src/getaddrinfo.h \
win32.mak groff2html Makefile.win32 \
AUTHORS ChangeLog INSTALL LICENSE README
.PHONY: test
test:
$(CC) $(CFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H \
-o test src/regression-test.c $(LDFLAGS) $(LIBS) -lpq -lpqtypes
@rm -f regression-test.o
.PHONY: docs
docs:
@find docs/man3 -name '*.3' -type f | xargs -i ./groff2html \{\}
uninstall:
rm -f ${prefix}/include/libpqtypes.h
rm -f ${prefix}/lib/libpqtypes.*
@if [ 1 ] ; then \
_manpath=`man -w PQgetf`; \
if test ! -z $$_manpath ; then \
manpath=`dirname $$_manpath`; \
for man in $(manpages); do \
man=`basename $$man`; \
if test -f $${manpath}/$$man ; then \
echo rm -f $${manpath}/$$man; \
rm -f $${manpath}/$$man; \
fi; \
done \
fi; \
fi;

View file

@ -0,0 +1,767 @@
# Makefile.in generated by automake 1.9.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = .
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
INSTALL = @INSTALL@
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
target_triplet = @target@
DIST_COMMON = README $(am__configure_deps) $(dist_man_MANS) \
$(include_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
$(top_srcdir)/configure $(top_srcdir)/src/pqt_config.h.in \
AUTHORS ChangeLog INSTALL config.guess config.sub install-sh \
ltmain.sh missing
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno configure.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/src/pqt_config.h
CONFIG_CLEAN_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)" \
"$(DESTDIR)$(includedir)"
libLTLIBRARIES_INSTALL = $(INSTALL)
LTLIBRARIES = $(lib_LTLIBRARIES)
libpqtypes_la_LIBADD =
am__objects_1 = array.lo datetime.lo error.lo events.lo exec.lo geo.lo \
handler.lo misc.lo network.lo numerics.lo param.lo port.lo \
record.lo spec.lo utils.lo varlena.lo
am_libpqtypes_la_OBJECTS = $(am__objects_1)
libpqtypes_la_OBJECTS = $(am_libpqtypes_la_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src
depcomp =
am__depfiles_maybe =
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(libpqtypes_la_SOURCES)
DIST_SOURCES = $(libpqtypes_la_SOURCES)
man3dir = $(mandir)/man3
NROFF = nroff
MANS = $(dist_man_MANS)
includeHEADERS_INSTALL = $(INSTALL_HEADER)
HEADERS = $(include_HEADERS)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
am__remove_distdir = \
{ test ! -d $(distdir) \
|| { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
&& rm -fr $(distdir); }; }
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = --best
distuninstallcheck_listfiles = find . -type f -print
distcleancheck_listfiles = find . -type f -print
ACLOCAL = @ACLOCAL@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
AMTAR = @AMTAR@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
ECHO = @ECHO@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
F77 = @F77@
FFLAGS = @FFLAGS@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBPQTYPES_SO_VERSION = @LIBPQTYPES_SO_VERSION@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
POW_LIB = @POW_LIB@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_F77 = @ac_ct_F77@
ac_ct_RANLIB = @ac_ct_RANLIB@
ac_ct_STRIP = @ac_ct_STRIP@
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
datadir = @datadir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
prefix = @prefix@
program_transform_name = @program_transform_name@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@
target = @target@
target_alias = @target_alias@
target_cpu = @target_cpu@
target_os = @target_os@
target_vendor = @target_vendor@
AM_LDFLAGS =
AUTOMAKE_OPTIONS = foreign
srcfiles = src/array.c src/datetime.c src/error.c \
src/events.c src/exec.c src/geo.c src/handler.c \
src/misc.c src/network.c src/numerics.c src/param.c \
src/port.c src/record.c src/spec.c src/utils.c src/varlena.c \
src/libpqtypes.h src/libpqtypes-int.h
manpages = docs/man3/PQgeterror.3 docs/man3/PQgetf.3 \
docs/man3/PQlocalTZInfo.3 docs/man3/PQparamClear.3 \
docs/man3/PQparamCreate.3 docs/man3/PQparamExec.3 \
docs/man3/PQparamExecPrepared.3 docs/man3/PQparamReset.3 \
docs/man3/PQparamSendQuery.3 docs/man3/PQparamSendQueryPrepared.3 \
docs/man3/PQputf.3 docs/man3/PQputvf.3 \
docs/man3/PQseterror.3 docs/man3/pqt-composites.3 docs/man3/pqt-handlers.3 \
docs/man3/PQgetErrorField.3 docs/man3/pqt-specs.3 \
docs/man3/PQtypesRegister.3 docs/man3/PQparamCount.3 \
docs/man3/PQexecf.3 docs/man3/PQexecvf.3 docs/man3/PQspecPrepare.3 \
docs/man3/PQsendf.3 docs/man3/PQsendvf.3 docs/man3/PQgetvf.3 \
docs/man3/PQregisterComposites.3 docs/man3/PQregisterUserDefinedTypes.3 \
docs/man3/PQregisterSubClasses.3 docs/man3/PQregisterTypes.3 \
docs/man3/PQregisterResult.3 docs/man3/PQinitTypes.3 \
docs/man3/PQclearTypes.3 docs/man3/PQclearSpecs.3
lib_LTLIBRARIES = libpqtypes.la
libpqtypes_la_SOURCES = $(srcfiles)
libpqtypes_la_LDFLAGS = -version-info $(LIBPQTYPES_SO_VERSION)
include_HEADERS = src/libpqtypes.h
dist_man_MANS = $(manpages)
noinst = src/libpqtypes-int.h src/getaddrinfo.h
EXTRA_DIST = install-sh src/regression-test.c src/getaddrinfo.h \
win32.mak groff2html Makefile.win32 \
AUTHORS ChangeLog INSTALL LICENSE README
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
am--refresh:
@:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
echo ' cd $(srcdir) && $(AUTOMAKE) --foreign --ignore-deps'; \
cd $(srcdir) && $(AUTOMAKE) --foreign --ignore-deps \
&& exit 0; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps Makefile'; \
cd $(top_srcdir) && \
$(AUTOMAKE) --foreign --ignore-deps Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
echo ' $(SHELL) ./config.status'; \
$(SHELL) ./config.status;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(SHELL) ./config.status --recheck
$(top_srcdir)/configure: $(am__configure_deps)
cd $(srcdir) && $(AUTOCONF)
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
src/pqt_config.h: src/stamp-h1
@if test ! -f $@; then \
rm -f src/stamp-h1; \
$(MAKE) src/stamp-h1; \
else :; fi
src/stamp-h1: $(top_srcdir)/src/pqt_config.h.in $(top_builddir)/config.status
@rm -f src/stamp-h1
cd $(top_builddir) && $(SHELL) ./config.status src/pqt_config.h
$(top_srcdir)/src/pqt_config.h.in: $(am__configure_deps)
cd $(top_srcdir) && $(AUTOHEADER)
rm -f src/stamp-h1
touch $@
distclean-hdr:
-rm -f src/pqt_config.h src/stamp-h1
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
if test -f $$p; then \
f=$(am__strip_dir) \
echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
$(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
else :; fi; \
done
uninstall-libLTLIBRARIES:
@$(NORMAL_UNINSTALL)
@set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
p=$(am__strip_dir) \
echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
$(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
done
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
test "$$dir" != "$$p" || dir=.; \
echo "rm -f \"$${dir}/so_locations\""; \
rm -f "$${dir}/so_locations"; \
done
libpqtypes.la: $(libpqtypes_la_OBJECTS) $(libpqtypes_la_DEPENDENCIES)
$(LINK) -rpath $(libdir) $(libpqtypes_la_LDFLAGS) $(libpqtypes_la_OBJECTS) $(libpqtypes_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
.c.o:
$(COMPILE) -c $<
.c.obj:
$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
$(LTCOMPILE) -c -o $@ $<
array.lo: src/array.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o array.lo `test -f 'src/array.c' || echo '$(srcdir)/'`src/array.c
datetime.lo: src/datetime.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o datetime.lo `test -f 'src/datetime.c' || echo '$(srcdir)/'`src/datetime.c
error.lo: src/error.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o error.lo `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c
events.lo: src/events.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o events.lo `test -f 'src/events.c' || echo '$(srcdir)/'`src/events.c
exec.lo: src/exec.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o exec.lo `test -f 'src/exec.c' || echo '$(srcdir)/'`src/exec.c
geo.lo: src/geo.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o geo.lo `test -f 'src/geo.c' || echo '$(srcdir)/'`src/geo.c
handler.lo: src/handler.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o handler.lo `test -f 'src/handler.c' || echo '$(srcdir)/'`src/handler.c
misc.lo: src/misc.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o misc.lo `test -f 'src/misc.c' || echo '$(srcdir)/'`src/misc.c
network.lo: src/network.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o network.lo `test -f 'src/network.c' || echo '$(srcdir)/'`src/network.c
numerics.lo: src/numerics.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o numerics.lo `test -f 'src/numerics.c' || echo '$(srcdir)/'`src/numerics.c
param.lo: src/param.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o param.lo `test -f 'src/param.c' || echo '$(srcdir)/'`src/param.c
port.lo: src/port.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o port.lo `test -f 'src/port.c' || echo '$(srcdir)/'`src/port.c
record.lo: src/record.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o record.lo `test -f 'src/record.c' || echo '$(srcdir)/'`src/record.c
spec.lo: src/spec.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o spec.lo `test -f 'src/spec.c' || echo '$(srcdir)/'`src/spec.c
utils.lo: src/utils.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils.lo `test -f 'src/utils.c' || echo '$(srcdir)/'`src/utils.c
varlena.lo: src/varlena.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o varlena.lo `test -f 'src/varlena.c' || echo '$(srcdir)/'`src/varlena.c
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
distclean-libtool:
-rm -f libtool
uninstall-info-am:
install-man3: $(man3_MANS) $(man_MANS)
@$(NORMAL_INSTALL)
test -z "$(man3dir)" || $(mkdir_p) "$(DESTDIR)$(man3dir)"
@list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
for i in $$l2; do \
case "$$i" in \
*.3*) list="$$list $$i" ;; \
esac; \
done; \
for i in $$list; do \
if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
else file=$$i; fi; \
ext=`echo $$i | sed -e 's/^.*\\.//'`; \
case "$$ext" in \
3*) ;; \
*) ext='3' ;; \
esac; \
inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
inst=`echo $$inst | sed -e 's/^.*\///'`; \
inst=`echo $$inst | sed '$(transform)'`.$$ext; \
echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \
$(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst"; \
done
uninstall-man3:
@$(NORMAL_UNINSTALL)
@list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
for i in $$l2; do \
case "$$i" in \
*.3*) list="$$list $$i" ;; \
esac; \
done; \
for i in $$list; do \
ext=`echo $$i | sed -e 's/^.*\\.//'`; \
case "$$ext" in \
3*) ;; \
*) ext='3' ;; \
esac; \
inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
inst=`echo $$inst | sed -e 's/^.*\///'`; \
inst=`echo $$inst | sed '$(transform)'`.$$ext; \
echo " rm -f '$(DESTDIR)$(man3dir)/$$inst'"; \
rm -f "$(DESTDIR)$(man3dir)/$$inst"; \
done
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
test -z "$(includedir)" || $(mkdir_p) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
$(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
done
uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
rm -f "$(DESTDIR)$(includedir)/$$f"; \
done
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
mkid -fID $$unique
tags: TAGS
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(CTAGS_ARGS)$$tags$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$tags $$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& cd $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) $$here
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
$(mkdir_p) $(distdir)/docs/man3 $(distdir)/src
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
case $$file in \
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
esac; \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
dir="/$$dir"; \
$(mkdir_p) "$(distdir)$$dir"; \
else \
dir=''; \
fi; \
if test -d $$d/$$file; then \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
fi; \
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
else \
test -f $(distdir)/$$file \
|| cp -p $$d/$$file $(distdir)/$$file \
|| exit 1; \
fi; \
done
-find $(distdir) -type d ! -perm -755 -exec chmod a+rwx,go+rx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r $(distdir)
dist-gzip: distdir
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
$(am__remove_distdir)
dist-bzip2: distdir
tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
$(am__remove_distdir)
dist-tarZ: distdir
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
$(am__remove_distdir)
dist-shar: distdir
shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
$(am__remove_distdir)
dist-zip: distdir
-rm -f $(distdir).zip
zip -rq $(distdir).zip $(distdir)
$(am__remove_distdir)
dist dist-all: distdir
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
$(am__remove_distdir)
# This target untars the dist file and tries a VPATH configuration. Then
# it guarantees that the distribution is self-contained by making another
# tarfile.
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
esac
chmod -R a-w $(distdir); chmod a+w $(distdir)
mkdir $(distdir)/_build
mkdir $(distdir)/_inst
chmod a-w $(distdir)
dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
&& dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
&& cd $(distdir)/_build \
&& ../configure --srcdir=.. --prefix="$$dc_install_base" \
$(DISTCHECK_CONFIGURE_FLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
&& $(MAKE) $(AM_MAKEFLAGS) check \
&& $(MAKE) $(AM_MAKEFLAGS) install \
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
&& $(MAKE) $(AM_MAKEFLAGS) uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
distuninstallcheck \
&& chmod -R a-w "$$dc_install_base" \
&& ({ \
(cd ../.. && umask 077 && mkdir "$$dc_destdir") \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
} || { rm -rf "$$dc_destdir"; exit 1; }) \
&& rm -rf "$$dc_destdir" \
&& $(MAKE) $(AM_MAKEFLAGS) dist \
&& rm -rf $(DIST_ARCHIVES) \
&& $(MAKE) $(AM_MAKEFLAGS) distcleancheck
$(am__remove_distdir)
@(echo "$(distdir) archives ready for distribution: "; \
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
distuninstallcheck:
@cd $(distuninstallcheck_dir) \
&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
|| { echo "ERROR: files left after uninstall:" ; \
if test -n "$(DESTDIR)"; then \
echo " (check DESTDIR support)"; \
fi ; \
$(distuninstallcheck_listfiles) ; \
exit 1; } >&2
distcleancheck: distclean
@if test '$(srcdir)' = . ; then \
echo "ERROR: distcleancheck can only run from a VPATH build" ; \
exit 1 ; \
fi
@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left in build directory after distclean:" ; \
$(distcleancheck_listfiles) ; \
exit 1; } >&2
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES) $(MANS) $(HEADERS)
installdirs:
for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(includedir)"; do \
test -z "$$dir" || $(mkdir_p) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
`test -z '$(STRIP)' || \
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
mostlyclean-am
distclean: distclean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-hdr distclean-libtool distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
info: info-am
info-am:
install-data-am: install-includeHEADERS install-man
install-exec-am: install-libLTLIBRARIES
install-info: install-info-am
install-man: install-man3
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf $(top_srcdir)/autom4te.cache
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-includeHEADERS uninstall-info-am \
uninstall-libLTLIBRARIES uninstall-man
uninstall-man: uninstall-man3
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
clean-generic clean-libLTLIBRARIES clean-libtool ctags dist \
dist-all dist-bzip2 dist-gzip dist-shar dist-tarZ dist-zip \
distcheck distclean distclean-compile distclean-generic \
distclean-hdr distclean-libtool distclean-tags distcleancheck \
distdir distuninstallcheck dvi dvi-am html html-am info \
info-am install install-am install-data install-data-am \
install-exec install-exec-am install-includeHEADERS \
install-info install-info-am install-libLTLIBRARIES \
install-man install-man3 install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-includeHEADERS \
uninstall-info-am uninstall-libLTLIBRARIES uninstall-man \
uninstall-man3
@SET_MAKE@
.PHONY: test
test:
$(CC) $(CFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H \
-o test src/regression-test.c $(LDFLAGS) $(LIBS) -lpq -lpqtypes
@rm -f regression-test.o
.PHONY: docs
docs:
@find docs/man3 -name '*.3' -type f | xargs -i ./groff2html \{\}
uninstall:
rm -f ${prefix}/include/libpqtypes.h
rm -f ${prefix}/lib/libpqtypes.*
@if [ 1 ] ; then \
_manpath=`man -w PQgetf`; \
if test ! -z $$_manpath ; then \
manpath=`dirname $$_manpath`; \
for man in $(manpages); do \
man=`basename $$man`; \
if test -f $${manpath}/$$man ; then \
echo rm -f $${manpath}/$$man; \
rm -f $${manpath}/$$man; \
fi; \
done \
fi; \
fi;
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

View file

@ -0,0 +1,49 @@
##############################################################
# Project: libpqtypes
# Makefile for Cygwin or Mingw Environments (GCC)
#
# make -f Makefile.win32 [options] [targets]
#
# For further build instructions, see the package's INSTALL file.
#
# Authors: Andrew Chernow, Merlin Moncure
# Contact: libpqtypes@esilo.com
##############################################################
PROJNAME = libpqtypes
DEPS = src/libpqtypes-int.h src/libpqtypes.h
OBJECTS = src/array.o src/datetime.o src/error.o \
src/events.o src/exec.o src/geo.o src/handler.o \
src/misc.o src/network.o src/numerics.o \
src/param.o src/port.o src/record.o src/spec.c \
src/utils.o src/varlena.o
INC += -Isrc
LIBS = -lpq -lws2_32
CFLAGS += -s -Wall -Wpointer-arith -D_GNU_SOURCE -O3 \
-std=gnu99 -Wlong-long -D_WIN32_WINNT=0x0501 -D_REENTRANT
ifdef MT
CFLAGS += -DPQT_THREAD_SAFE -D_THREAD_SAFE
LIBS += -lpthread
endif
ifdef PQT_LONG_LONG
CFLAGS += -DPQT_LONG_LONG=$(PQT_LONG_LONG)
endif
all: $(OBJECTS)
dllwrap -o $(PROJNAME).dll -dllname $(PROJNAME).dll $(OBJECTS) $(LPATH) $(LIBS)
dlltool --dllname $(PROJNAME).dll --output-lib $(PROJNAME).a
test:
gcc $(CFLAGS) $(INC) -o regtest src/regression-test.c $(PROJNAME).dll $(LPATH) $(LIBS)
-@rm -f regression-test.o
%.o: %.c $(DEPS)
gcc $(CFLAGS) $(INC) -o $@ -c $<
clean:
-@rm -f $(OBJECTS) $(PROJNAME).so $(PROJNAME).a regtest

View file

@ -0,0 +1,54 @@
LIBPQTYPES LIBRARY
------------------
This package contains the source code, documentation and management
files for the libpqtypes library, an extension to the PostgreSQL
libpq library.
libpqtypes is a libpq extension that offers a new way of handling
parameterized queries and getting result field values. Both
putting parameters and getting values use a printf/scanf style
interface, with consistent specifiers for both.
- Full support for binary and text format (parameter and result)
- Full support for composites, arrays and composite arrays.
- printf style interface to libpq's binary parameterized API.
- scanf style interface for getting values: PQgetvalue extension.
- Ability to register user-defined types, aliases and data type
sub-classes for use with printf style interfaces: ex. "%mytype".
- Per-thread global error message: PQgeterror, PQseterror.
- Online docs as well as man pages.
INSTALL
libpqtypes cannot be used by itself, it can only be used with libpq.
To build and install libpqtypes, see the INSTALL file included with
this package.
LICENSE
libpqtypes is released under the BSD license just like all software
components of the PostgreSQL Database Management System. See the
LICENSE file included with this package.
AUTHORS
libpqtypes is a contribution of eSilo, LLC and was written by
Andrew Chernow and Merlin Moncure. See the AUTHORS file.
To report bugs, feature requests or general questions goto the
libpqtypes forum at http://pgfoundry.org/forum/?group_id=1000370
or send an email to <libpqtypes@esilo.com>.
Online documentation:
http://libpqtypes.esilo.com/
The latest version:
http://pgfoundry.org/projects/libpqtypes/

View file

@ -0,0 +1,139 @@
AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_C
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC
"-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
dnl the only difference - the LANG selection... and the default FLAGS
AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_NEW], [dnl
AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$1])dnl
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC
"-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])])
AC_DEFUN([AX_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
[AX_CXXFLAGS_GCC_OPTION_NEW($@)],[AX_CXXFLAGS_GCC_OPTION_OLD($@)])])
AC_DEFUN([AC_C_LONG_LONG],
[AC_CACHE_CHECK(for long long int, ac_cv_c_long_long,
[if test "$GCC" = yes; then
ac_cv_c_long_long=yes
else
AC_TRY_COMPILE(,[long long int i;],
ac_cv_c_long_long=yes,
ac_cv_c_long_long=no)
fi])
if test $ac_cv_c_long_long = yes; then
AC_DEFINE(HAVE_LONG_LONG, 1, [compiler understands long long])
fi
])
dnl AC_CHECK_FUNC_IN(HEADER, FUNCTION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
AC_DEFUN([AC_CHECK_FUNC_IN],
[AC_MSG_CHECKING([for $2 in $1])
AC_CACHE_VAL(ac_cv_func_$2,
[AC_TRY_LINK(
dnl Don't include <ctype.h> because on OSF/1 3.0 it includes <sys/types.h>
dnl which includes <sys/select.h> which contains a prototype for
dnl select. Similarly for bzero.
[/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $2(); below. */
#include <assert.h>
#include <$1>
/* Override any gcc2 internal prototype to avoid an error. */
]ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus
extern "C"
#endif
])dnl
[/* We use char because int might match the return type of a gcc2
builtin and then its argument prototype would still apply. */
char $2();
], [
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined (__stub_$2) || defined (__stub___$2)
choke me
#else
$2();
#endif
], eval "ac_cv_func_$2=yes", eval "ac_cv_func_$2=no")])
if eval "test \"`echo '$ac_cv_func_'$2`\" = yes"; then
AC_MSG_RESULT(yes)
ifelse([$3], , :, [$3])
else
AC_MSG_RESULT(no)
ifelse([$4], , , [$4
])dnl
fi
])
dnl AC_CHECK_FUNCS_IN(HEADER, FUNCTION... [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
AC_DEFUN([AC_CHECK_FUNCS_IN],
[for ac_func in $2
do
AC_CHECK_FUNC_IN($1, $ac_func,
ac_tr_func=HAVE_`echo $ac_func | sed -e 'y:abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:' -e 's:[[^A-Z0-9]]:_:g'`
AC_DEFINE_UNQUOTED($ac_tr_func) $3], $4)dnl
done
])

7262
lib/libpqtypes-1.5.1/aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load diff

1411
lib/libpqtypes-1.5.1/config.guess vendored Normal file

File diff suppressed because it is too large Load diff

1500
lib/libpqtypes-1.5.1/config.sub vendored Normal file

File diff suppressed because it is too large Load diff

24850
lib/libpqtypes-1.5.1/configure vendored Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.57)
AC_INIT(libpqtypes, 1.5.1, libpqtypes@esilo.com)
AC_CANONICAL_TARGET
AC_PROG_MAKE_SET
SET_MAKE="MAKE=make --no-print-directory"
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE(libpqtypes,1.5.1)
AC_SUBST([LIBPQTYPES_SO_VERSION], [1:5:1])
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
AC_PROG_INSTALL
AC_CONFIG_SRCDIR([src/param.c])
AM_CONFIG_HEADER([src/pqt_config.h:src/pqt_config.h.in])
AC_PROG_CXX
AC_PROG_CC
CPPFLAGS="$CPPFLAGS -Isrc"
if test "$CC" = "gcc" ; then
# always want reentrant funcs, not just thread-safe mode. -s
CFLAGS="$CFLAGS -O3 -Wall -Wpointer-arith -D_GNU_SOURCE -D_REENTRANT -fsigned-char"
# gcc doesn't indicate an error for unknown options when they are
# not warning/feature options, -Wxxx or -fxxx. We have to test manually.
cprog="int main(void){return 0;}"
gcctest="gcc -x c -c - -o /dev/null"
AC_MSG_CHECKING(CFLAGS for gcc -std=gnu99...)
result=`echo "$cprog" | $gcctest -std=gnu99 2>&1`
if test -z "$result" ; then
CFLAGS="$CFLAGS -std=gnu99"
AC_MSG_RESULT(-std=gnu99)
else
AC_MSG_RESULT([no, unknown])
fi
# turn on as many warnings as possible
AX_CFLAGS_GCC_OPTION([-Wclobbered])
AX_CFLAGS_GCC_OPTION([-Wempty-body])
AX_CFLAGS_GCC_OPTION([-Wignored-qualifiers])
#AX_CFLAGS_GCC_OPTION([-Wmissing-field-initializers]) # annoying
AX_CFLAGS_GCC_OPTION([-Wmissing-parameter-type])
AX_CFLAGS_GCC_OPTION([-Wold-style-declaration])
AX_CFLAGS_GCC_OPTION([-Woverride-init])
AX_CFLAGS_GCC_OPTION([-Wsign-compare])
AX_CFLAGS_GCC_OPTION([-Wtype-limits])
AX_CFLAGS_GCC_OPTION([-Wuninitialized])
AX_CFLAGS_GCC_OPTION([-fomit-frame-pointer])
AX_CFLAGS_GCC_OPTION([-fno-strict-aliasing])
AX_CFLAGS_GCC_OPTION([-funroll-all-loops])
AX_CFLAGS_GCC_OPTION([-funit-at-a-time])
else
AC_MSG_ERROR([Using $CC instead of gcc, currently not supported])
fi
# Each case has an example of what target_os contains
case ${target_os} in
# solaris
solaris*)
CFLAGS="$CFLAGS -D_STDC_C99"
;;
# SCO UnixWare
*UnixWare*|*unixware*)
CFLAGS="$CFLAGS -D__UnixWare__"
;;
# SCO OpenServer
sco*)
CFLAGS="$CFLAGS -D__OpenServer__"
;;
esac
AC_ARG_ENABLE(thread-safety, [ --enable-thread-safety Enable threads],
[have_threads=yes], [have_threads=no])
if test "$have_threads" = "yes" ; then
AC_SEARCH_LIBS(pthread_create, [pthread thread],,
[AC_MSG_ERROR([Missing libpthread.so or libthread.so, cannot use --enable-thread-safety])])
CFLAGS="$CFLAGS -DPQT_THREAD_SAFE -D_THREAD_SAFE"
# SCO OpenServer 5, possibly other platforms, requires using FSU pthreads.
# We detect this by looking at the prototype for pthread_getspecific.
# There are a few other prototypes that are different as well but detecting
# one is plenty.
AC_MSG_CHECKING(whether FSU pthreads is being used)
AC_TRY_COMPILE(
[#include <pthread.h>],
[pthread_getspecific((pthread_key_t)(0), (void *)(0))],
[AC_DEFINE(PTHREAD_FSU, 1,
[Define to 1 if using FSU pthreads]) AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)]
)
fi
# If strict memory alignment is required, this test will produce a
# Bus error. Most riscs CPUs (old and modern) are strict.
save_CFLAGS=$CFLAGS
CFLAGS=""
AC_MSG_CHECKING(if strict memory alignment is required)
AC_RUN_IFELSE([int main(void){char b[[8]];int*i=(int*)(b+1);*i=0;return 0;}],
[AC_MSG_RESULT(no)], [AC_DEFINE(STRICT_MEMORY_ALIGNMENT, 1,
[Define to 1 if architecture requires strict memory alignment]) AC_MSG_RESULT(yes)]
)
CFLAGS=$save_CFLAGS
AC_HEADER_STDC
AC_CHECK_HEADERS([ \
arpa/inet.h limits.h linux/limits.h netdb.h netinet/in.h stddef.h \
sys/socket.h time.h math.h sys/time.h strings.h])
AC_STRUCT_TIMEZONE
AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,, \
[#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif])
AC_CHECK_TYPES(socklen_t,,,
[#include <unistd.h>
#ifdef HAVE_APRA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif])
AC_CHECK_MEMBERS([struct sockaddr_storage.ss_len],,, \
[#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif])
AC_C_CONST
AC_FUNC_MEMCMP
AC_FUNC_STRTOD
AC_HEADER_TIME
AC_TYPE_SIZE_T
AC_SEARCH_LIBS(pow, m)
AC_SEARCH_LIBS(getaddrinfo, [nsl socket], \
[AC_DEFINE([HAVE_GETADDRINFO], [], \
[Define if getaddrinfo exists])], [], [])
AC_CHECK_FUNCS([vsnprintf floor ceil rint hstrerror \
localtime_r strtol strtoll strtoul])
AC_OUTPUT(Makefile)

View file

@ -0,0 +1 @@
.so man3/PQspecPrepare.3

View file

@ -0,0 +1 @@
.so man3/PQinitTypes.3

View file

@ -0,0 +1,104 @@
.TH "PQexecf" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQexecf \- Prepares parameters and executes a command.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
PGresult *PQexecf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, ...);
.br
PGresult *PQexecvf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, va_list \fIap\fP);
.br
int PQsendf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, ...);
.br
int PQsendvf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, va_list \fIap\fP);
\fP
.SH DESCRIPTION
.LP
The \fIPQexecf\fP() function executes a command that uses libpqtypes
type specifiers instead of $1, $2, etc... syntax. The idea
is to combine \fIPQputvf\fP() and \fIPQparamExec\fP() into a single call.
The variable argument list must match the type specs listed within the
cmd. The type specifiers should be placed where one would normally place
$1, $2, etc...
The \fIPQexecvf\fP() function is identical to \fIPQexecf\fP() except
it takes a va_list.
The \fIPQsendf\fP() and \fIPQsendvf\fP() functions are identical to
\fIPQexecf\fP() and \fIPQexecvf\fP() except they are asynchronous versions,
meaning an additional call, PQgetResult, must be issued to get the PGresult
object. For more information, see the PostgreSQL Documentation: specifically
the "Asynchronous Command Processing" section under
"Client Interfaces | libpq - C library".
A prepared type spec "@name" can be used with these functions, granted that
\fIPQspecPrepare\fP() was called with a non-zero value for the is_stmt
argument. These functions needs an actual SQL statement to execute.
.SH RETURN VALUE
.LP
PQexecf and PQexecvf return NULL on error and a valid PGresult on success.
PQsendf and PQsendvf return zero on error and a non-zero value on success.
On error, use \fIPQgeterror(3)\fP to obtain an error message.
.SH EXAMPLES
.LP
.SS Using PQexecf
The example uses PQexecf function to execute a query.
.RS
.nf
.LP
\fB
/* The PQexecf call is shorthand for:
*
* PGparam *param = PQparamCreate(conn);
* PQputf(param, "%int4 %int4", 1, 1);
* res = PQparamExec(conn, param, "SELECT $1 + $2", 1);
* PQparamClear(param);
*
* As you may notice, PQexecf makes life much simpler.
*/
PGresult *res = PQexecf(conn, "SELECT %int4 + %int4", 1, 1);
if(!res)
fprintf(stderr, "PQexecf failed: %s", PQgeterror());
else
PQclear(res);
/* A bit more common, this puts an int4 and a text into a generated
* PGparam and then executes 'myfunc($1, $2)'
*/
res = PQexecf(conn, "SELECT * FROM myfunc(%int4, %text)", 2, "abc");
/* Prepared type spec example. To use with execf, the final "is_stmt"
* argument must be set to a non-zero value!
*/
PQspecPrepare(conn, "account_insert", "INSERT INTO account VALUES "
"(%int8, %text, %int4)", 1);
PGint8 acc_id = 78236;
PGtext acc_name = "ABC Coders, LLC.";
PGint4 acc_biz = ACC_BIZ_TECHNOLOGY;
PQexecf(conn, "@account_insert", acc_id, acc_name, acc_biz);
\fP
.fi
.RE
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIPQgeterror(3)\fP, \fIPQputvf(3)\fP, \fIPQparamExec(3)\fP

View file

@ -0,0 +1 @@
.so man3/PQexecf.3

View file

@ -0,0 +1 @@
.so man3/PQgeterror.3

View file

@ -0,0 +1,56 @@
.TH "PQgeterror" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQgeterror, PQseterror, PQgetErrorField \- libpqtypes error system management functions
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
char *PQgeterror(void);
.br
void PQseterror(const char *\fIformat\fP, ...);
.br
char *PQgetErrorField(int \fIfieldcode\fP);
\fP
.SH DESCRIPTION
.LP
The libpqtypes library maintains a per-thread global error message. The error
message is managed by \fIPQgeterror\fP() and \fiPQseterror\fP().
\fiPQseterror\fP() takes a regular printf compatible \fIformat\fP string for
setting the libpqtypes error message.
\fiPQgetErrorField\fP() gets an error field from the last executed query.
The error field is only set by PQparamExec and PQparamExecPrepared. When
using libpq functions that return results, like PQexec, continue to use
PQresultErrorField.
To clear the libpqtypes error message, call \fiPQseterror\fP() with a NULL
\fIformat\fP string: PQseterror(NULL); This will also clear error fields.
.SH RETURN VALUE
.LP
\fIPQgeterror\fP() returns the last error message for the calling thread. It will
never return NULL.
.LP
\fIPQgetErrorField\fP() returns the error field, referenced by fieldcode, for
the last executed query. NULL is returned if no value exists.
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-specs(3)\fP

View file

@ -0,0 +1,104 @@
.TH "PQgetf" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQgetf \- Gets one or more values from a PGresult in a scanf fashion.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQgetf(const PGresult *\fIres\fP, int \fItup_num\fP,
.br
const char *\fIformat\fP, ...);
.br
int PQgetvf(const PGresult *\fIres\fP, int \fItup_num\fP,
.br
const char *\fIformat\fP, va_list ap);
\fP
.SH DESCRIPTION
.LP
The \fIPQgetf\fP() and \fIPQgetvf\fP() functions get one or more field
values from a PGresult using a scanf style interface. The \fItup_num\fP
argument indicates which tuple to read values from; values can
only be read from one tuple at a time.
The \fIformat\fP argument is a data type specifier string
indicating the values to retrieve, like %int4 or #timestamp. Any
number of fields, in any order, can be retrieved in a single call.
Each data type specifier has a cooresponding \fBfield_num, type-args, [...]\fP
contained within the function\'s variable argument list. The
\fBfield_num\fP either indicates tuple field number or tuple field
name, depending on whether the data type in \fIformat\fP used a \'%\'
or \'#\' specifer mark (\`man \fIpqt-specs(3)\fP\'). The \fBtype-args\fP can be one or more
arguments required by the specific data type: for example, "%int4"
would require a pointer to a PGint4. All built-in PostgreSQL data
types require a single pointer value.
.SH RETURN VALUE
.LP
On success, a non-zero value is returned. On error, zero is
returned and \fIPQgeterror(3)\fP will contain an error message.
If the retrieval of any field fails, the get operation is aborted and
function will fail. Eventhough some fields may have succeeded prior to
the failure, their values should not be trusted. Instead, make
another \fIPQgetf\fP() call. Getting an array or a composite can lead
to memory leaks when getf fails. This is because these types allocate
a PGresult object that must be cleared. To avoid leaks, it is recommended
to perform cleanup even if getf fails, retrieve arrays and composites by
themselves or make them the last field in a getf call.
.SH EXAMPLES
.LP
.SS Using PQgetf on a PGresult
The example uses the libpq PQexec function to execute a query and then uses
\fIPQgetf\fP() to retrieve field values. It is important to note that
libpqtypes execution functions, like \fIPQparamExec(3)\fP, do not need to
generate the PGresult provided to \fIPQgetf\fP().
.RS
.nf
.LP
\fBint success;
PGint4 i4;
PGtext text;
PGbytea bytea;
PGpoint pt;
PGresult *res = PQexec(conn, "SELECT i,t,b,p FROM tbl");
/* Get some field values from the result (order doesn\'t matter) */
success = PQgetf(res,
0, /* get field values from tuple 0 */
"%int4 #text %bytea %point",
/* type format specifiers (get text by name \'#\') */
0, &i4, /* get an int4 from field num 0 */
"t", &text, /* get a text from field name "t" */
2, &bytea, /* get a bytea from field num 2 */
3, &pt); /* get a point from field num 3 */
/* Output an error message using PQgeterror(3). */
if(!success)
fprintf(stderr, "*ERROR: %s\\n", PQgeterror());
/* Output the values, do this before PQclear() */
else
printf("int4=%d, text=%s, bytea=%d bytes, point=(%f,%f)\\n",
i4, text, bytea.len, pt.x, pt.y);
PQclear(res);
\fP
.fi
.RE
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-specs(3)\fP, \fIPQgeterror(3)\fP

View file

@ -0,0 +1 @@
.so man3/PQgetf.3

View file

@ -0,0 +1,56 @@
.TH PQinitTypes 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQinitTypes \- Initializes libpqtypes with the libpq event system.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQinitTypes(PGconn *conn);
.br
int PQclearTypes(PGconn *\fIconn\fP);
\fP
.SH DESCRIPTION
.LP
libpqtypes makes use of the libpq Event System. Before using
libpqtypes, you must initialize libpqtypes as a libpq EventProc.
The PQinitTypes function takes a PGconn that libpqtypes will be initialized
with; each PGconn requires its own initialization.
PQclearTypes removes all registered types from the given PGconn. A good use
for this is after a PQresetXXX call when it might be desired to re-register
all types that may have gone stale.
.SH RETURN VALUE
.LP
PQinitTypes and PQclearTypes return zero if it fails and non-zero if it succeeds.
.SH EXAMPLES
.LP
.SS Initializing libpqtypes
The examples shows how to initialize libpqtypes with the libpq event system.
.RS
.nf
.LP
\fB/* call prior to any other libpqtypes functions that operate on conn. */
PQinitTypes(conn);
\fP
.fi
.RE
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
None

View file

@ -0,0 +1,66 @@
.TH "PQlocalTZInfo" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQlocalTZInfo \- Gets the local machine\'s timezone information.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
void PQlocalTZInfo(time_t *\fIt\fP, int *\fIgmtoff\fP, int *\fIisdst\fP, char **\fItzabbrp\fP);
\fP
.SH DESCRIPTION
.LP
This function retrieves the local machine\'s timezone information.
If the \fIt\fP argument is not NULL, it represents a time_t value to get timezone
information for. If it is NULL, the current time is used.
The \fIgmtoff\fP argument will be pointed at the number of seconds from GMT,
same value as the GNU (struct tm).tm_gmtoff extension.
The \fIisdst\fP argument will be pointed at zero if in standard time, one if in
daylight savings time and negative one if unknown.
The \fItzabbrp\fP argument will be pointed at the timezone abbreviation, like
PST, ADT, EST, etc..
.SH RETURN VALUE
.LP
None.
.SH EXAMPLES
.LP
This example gets the local timezone information for a file\'s modified time.
.LP
.RS
.nf
\fBint gmtoff;
int isdst;
char *tzabbr;
struct stat st;
stat("report.xml", &st);
PQlocalTZInfo(&st.st_mtime, &gmtoff, &isdst, &tzabbr);
\fP
.fi
.RE
.SH RATIONALE
.LP
libpqtypes needs the ability to get the local machine\'s timezone information for the
datetime data types. It later became apparent that a portable way of getting timezone
information was very useful and in demand. Thus, this function was made public.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-specs(3)\fP

View file

@ -0,0 +1 @@
.so man3/PQparamCreate.3

View file

@ -0,0 +1 @@
.so man3/PQparamCreate.3

View file

@ -0,0 +1,76 @@
.TH "PQparamCreate" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQparamCreate, PQparamCount PQparamReset, PQparamClear \- PGparam management functions.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
PGparam *PQparamCreate(const PGconn *\fIconn\fP);
.br
PGparam *PQparamDup(PGparam *\fIparam\fP);
.br
int PQparamCount(PGparam *\fIparam\fP);
.br
void PQparamReset(PGparam *\fIparam\fP);
.br
void PQparamClear(PGparam *\fIparam\fP);
\fP
.SH DESCRIPTION
.LP
These functions manage the opaque PGparam object.
\fIPQparamCreate\fP() will allocate and initialize a new PGparam object.
After the create call, the PGparam object is ready for use. WARNING: Only
types that have been registered via PQregisterXXX, will be available
to the param. Meaning, the param is not updated with types registered
after the param is created.
\fIPQparamDup\fP() will duplicate a given PGparam, including any internal
values that have already been put. This is useful in cases where you want
to queue qeuries to execute at a later time, like a connection pooler. The
problem is PGparamCreate requires a connection object, which may not be
available when attempting to enqueue a query with its PGparam object.
Instead, a PGparam object can used for the sole purpose of creating
duplicates while there are no available PGconn objects.
\fIPQparamCount\fP() gets the number of parameters in a PGparam object.
\fIPQparamReset\fP() will clear out any previously put parameters, but will
not free any memory. This is useful for application looking to "reuse" a
PGparam object.
\fIPQparamClear\fP() releases all resources being used by a PGparam object,
the object should not be used after a clear.
It is very important to call \fIPQparamReset\fP() if you plan on reusing a
PGparam object.
.SH RETURN VALUE
.LP
\fIPQparamCreate\fP() returns a pointer to a PGparam object on success and NULL
if something failed (check \fIPQgeterror(3)\fP for more information).
\fIPQparamCount\fP() returns the number of parameters in a PGparam object.
\fIPQparamReset\fP() and \fIPQparamClear\fP() have no return values. If
either function is provided a NULL PGparam pointer, it will silently fail.
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-specs(3)\fP, \fIPQputf(3)\fP, \fIPQgeterror(3)\fP, \fIPQparamExec(3)\fP

View file

@ -0,0 +1,106 @@
.TH "PQparamExec" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQparamExec, PQparamExecPrepared \- Executes a paramertized query using the parameters in a PGparam.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
PGresult *PQparamExec(PGconn *\fIconn\fP, PGparam *\fIparam\fP,
.br
const char *\fIcommand\fP, int \fIresultFormat\fP);
.br
PGresult *PQparamExecPrepared(PGconn *\fIconn\fP, PGparam *\fIparam\fP,
.br
const char *\fIstmtName\fP, int \fIresultFormat\fP);
\fP
.SH DESCRIPTION
.LP
The \fIPQparamExec\fP() and \fIPQparamExecPrepared\fP() functions execute
a parameterized query using the parameters in a PGparam. The only difference
between these functions is that \fIPQparamExec\fP() expects a parameterized
\fIcommand\fP string while \fIPQparamExecPrepared\fP() expects a \fIstmtName\fP
previously prepared via PQprepare().
Both functions take a \fIparam\fP argument, which must contain the same number
of parameters as either the \fIcommand\fP string or previously prepared \fIstmtName\fP.
Internally, the \fIparam\fP is transformed into parallel arrays that are
supplied to a PQexecParams() or PQexecPrepared() call.
The \fIresultFormat\fP argument indicates if text or binary results are desired;
a value of zero or one respectively. \fIPQgetf\fP supports both text and binary
result formats, with the exclusion of arrays and composites which only support binary.
.SH RETURN VALUE
.LP
On success, a pointer to a PGresult is returned. On error, NULL is
returned and \fIPQgeterror(3)\fP will contain an error message.
\fBIMPORTANT!\fP
.br
There is a difference in behavior between \fIPQparamExec\fP() and \fIPQparamExecPrepared\fP()
and the libpq functions they wrap, PQexecParams() and PQexecPrepared().
\fIPQparamExec\fP() and \fIPQparamExecPrepared\fP() only return a non-NULL
PGresult when the result status is either PGRES_COMMAND_OK, PGRES_TUPLES_OK or
PGRES_EMPTY_QUERY. If these functions detect any other result status, the
PGresult is cleared and a NULL result is returned. Before clearing the PGresult
and returning NULL, these functions first copy the result error message into the
libpqtypes error system, accessible via \fIPQgeterror(3)\fP. This allows applications
to get a result\'s error message without needing the result object. \fIconn\fP error
messages are also copied to the libpqtypes error system.
This behavior difference provides a single error indicator, a NULL return, and a
single function that can get the error message, \fIPQgeterror\fP().
.SH EXAMPLES
.LP
.SS Using PQparamExec
The example uses \fIPQparamExec\fP() to execute a query using a PGparam.
The example also demonstrates how to detect a failed exec and output an error
message.
.RS
.nf
.LP
\fBPGparam *param = PQparamCreate(conn);
if(!PQputf(param, "%text %int4", "ACTIVE", CAT_CAR))
{
fprintf(stderr, "PQputf: %s\\n", PQgeterror());
}
else
{
PGresult *res = PQparamExec(conn, param,
"SELECT * FROM t WHERE status=$1 AND category=$2", 1);
if(!res)
fprintf(stderr, "PQparamExec: %s\\n", PQgeterror());
else
print_results(res);
PQclear(res);
}
PQparamClear(param);
\fP
.fi
.RE
.SS Using PQparamExecPrepared
\fIPQparamExecPrepared\fP() is behaves identically to \fIPQparamExec\fP(), except
\fIPQparamExecPrepared\fP() requires that a statement has been previously prepared
via PQprepare(). Also, a \fIstmtName\fP is supplied rather than a parameterized
\fIcommand\fP string.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIPQparamCreate(3)\fP, \fIPQparamSendQuery(3)\fP, \fIPQparamSendQueryPrepared(3)\fP

View file

@ -0,0 +1 @@
.so man3/PQparamExec.3

View file

@ -0,0 +1 @@
.so man3/PQparamCreate.3

View file

@ -0,0 +1,89 @@
.TH "PQparamSendQuery" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQparamSendQuery, PQparamSendQueryPrepared \- Executes an asynchronous paramertized query using the parameters in a PGparam.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQparamSendQuery(PGconn *\fIconn\fP, PGparam *\fIparam\fP,
.br
const char *\fIcommand\fP, int \fIresultFormat\fP);
.br
int PQparamSendQueryPrepared(PGconn *\fIconn\fP, PGparam *\fIparam\fP,
.br
const char *\fIstmtName\fP, int \fIresultFormat\fP);
\fP
.SH DESCRIPTION
.LP
The \fIPQparamSendQuery\fP() and \fIPQparamSendQueryPrepared\fP() functions execute
an asynchronous paramertized query using the parameters in a PGparam. The only difference
between these functions is that \fIPQparamSendQuery\fP() expects a parameterized
\fIcommand\fP string while \fIPQparamSendQueryPrepared\fP() expects a \fIstmtName\fP
previously prepared via PQprepare().
Both functions take a \fIparam\fP argument, which must contain the same number
of parameters as either the \fIcommand\fP string or previously prepared \fIstmtName\fP.
Internally, the \fIparam\fP is transformed into parallel arrays that are
supplied to a PQsendQueryParams() or PQsendQueryPrepared() call.
The \fIresultFormat\fP argument indicates if text or binary results are desired;
a value of zero or one respectively. \fIPQgetf\fP supports both text and binary
result formats, with the exclusion of arrays and composites which only support binary.
After successfully calling \fIPQparamSendQuery\fP() or \fIPQparamSendQueryPrepared\fP(),
call libpq\'s PQgetResult() one or more times to obtain the results.
.SH RETURN VALUE
.LP
On success, a non-zero value is returned. On error, zero is
returned and \fIPQgeterror(3)\fP will contain an error message.
.SH EXAMPLES
.LP
.SS Using PQparamSendQuery
The example uses \fIPQparamSendQuery\fP() to execute a query using a PGparam.
.RS
.nf
.LP
\fBPGparam *param = PQparamCreate(conn);
if(!PQputf(param, "%text %int4", "ACTIVE", CAT_CAR))
{
fprintf(stderr, "PQputf: %s\\n", PQgeterror());
}
else
{
int success = PQparamSendQuery(conn, param,
"SELECT * FROM t WHERE status=$1 AND category=$2", 1);
if(!success)
fprintf(stderr, "PQparamSendQuery: %s\\n", PQgeterror());
else
get_and_print_results(conn); /* calls PQgetResult() */
}
PQparamClear(param);
\fP
.fi
.RE
.SS Using PQparamSendQueryPrepared
\fIPQparamSendQueryPrepared\fP() is behaves identically to \fIPQparamSendQuery\fP(), except
\fIPQparamSendQueryPrepared\fP() requires that a statement has been previously prepared
via PQprepare(). Also, a \fIstmtName\fP is supplied rather than a parameterized
\fIcommand\fP string.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIPQparamCreate(3)\fP, \fIPQgeterror(3)\fP, \fIPQparamExec(3)\fP, \fIPQparamExecPrepared(3)\fP

View file

@ -0,0 +1 @@
.so man3/PQparamSendQuery.3

View file

@ -0,0 +1,117 @@
.TH "PQputf" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQputf, PQputvf \- Packs a set of parameters in a PGparam for use with a parameterized query.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQputf(PGparam *\fIparam\fP, const char *\fIformat\fP, ...);
.br
int PQputvf(PGparam *\fIparam\fP, char *\fIstmtBuf\fP, size_t \fIstmtBufLen\fP,
.br
const char *\fIformat\fP, va_list \fIap\fP);
\fP
.SH DESCRIPTION
.LP
The \fIPQputf\fP() and \fIPQputvf\fP() functions put one or more query
parameters into a PGparam using a printf style interface. Any number
of parameters can be put at the same time.
The \fIformat\fP argument is a data type specifier string indicating the
parameters being put, such as "%int4 %polygon". \fIformat\fP cannot be
NULL or an empty string. The variable argument list must match the
\fIformat\fP, either "..." or \fIap\fP. The number of arguments required
for each data type is dependant on the data type itself; built-in
PostgreSQL types always require a single argument.
The \fIPQputvf\fP() function can construct a parameterized command string
from \fIformat\fP, as long as \fIstmtBuf\fP and \fIstmtBufLen\fP have been
provided. If the construction of \fIstmtBuf\fP is not desired, set it
to NULL and set \fIstmtBufLen\fP to 0. When a constructed statement is desired,
the contents of \fIformat\fP will be copied to \fIstmtBuf\fP and
all data type specifiers, like "%int4", will be replaced with $1, $2, etc...
syntax. The result is a parameterized statement in synch with \fIparam\fP
and ready to be executed. For instance: if \fIspec\fP is "SELECT %int4 + %int4",
the resulting \fIstmtBuf\fP would be "SELECT $1 + $2".
.SH RETURN VALUE
.LP
On success, a non-zero value is returned. On error, zero is
returned and \fIPQgeterror(3)\fP will contain an error message.
If any put operation fails, the \fIparam\fP is reverted back to the number
of parameters it had prior to the function call; partial puts are not allowed.
.SH EXAMPLES
.LP
.SS Using PQputf
The example uses \fIPQputf\fP() to put a couple parameters into a PGparam.
.RS
.nf
.LP
\fBPGtext text = "foobar";
PGint8 i8 = PQT_INT64CONST(1099511627776);
PGparam *param = PQparamCreate(conn);
if(!PQputf(param, "%text %int8", text, i8))
fprintf(stderr, "*ERROR: %s\\n", PQgeterror());
PQparamClear(param);
\fP
.fi
.RE
.SS Using PQputvf
The example creates an application function named execf. execf is a
wrapper to \fIPQputvf\fP(), as well as \fIPQparamExec(3)\fP. It is
similar to \fIPQexecf\fP(). The only difference is that the PQexecf
implementation uses a smaller stack buffer and allocates heap memory
when needed.
.RS
.nf
.LP
\fBPGresult *
execf(PGconn *conn, const char *format, ...)
{
va_list ap;
char stmt[32768];
PGparam *param;
PGresult *res = NULL;
/* create the temporary PGparam */
if(!(param = PQparamCreate(conn)))
return NULL; /* PQseterror already called */
/* put the params, create the stmt and exec it */
va_start(ap, format);
if(PQputvf(param, stmt, sizeof(stmt), format, ap))
res = PQparamExec(conn, param, stmt, 1); // resfmt is binary
va_end(ap);
/* param must be cleared */
PQparamClear(param);
return res;
}
/* Example: execf will put 2 ints and execute "SELECT $1 + $2" */
PGresult *res = execf(conn, "SELECT %int4 + %int4", 100, 67);
if(!res)
fprintf(stderr, "*ERROR: %s\\n", PQgeterror());
\fP
.fi
.RE
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-specs(3)\fP, \fIPQparamCreate(3)\fP, \fIPQgeterror(3)\fP, \fIPQseterror(3)\fP

View file

@ -0,0 +1 @@
.so man3/PQputf.3

View file

@ -0,0 +1,80 @@
.TH "PQregisterComposites" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQregisterComposites \- Registers a composites.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
int PQregisterComposites(PGconn *\fIconn\fP, PGregisterType *\fItypes\fP,
.br
int \fIcount\fP);
\fP
.SH DEPRECATED
.LP
THIS FUNCTION IS DEPRECATED. New applications should use PQregisterTypes.
This function is now a wrapper to PQregisterTypes.
.SH DESCRIPTION
.LP
The \fIPQregisterComposites\fP() function allows an application
to register one or more composites. This function can be
called as many times as an application needs on a PGconn.
This function must execute a query against the backend to retrieve type
information for each composite, thus this should not be called from within
a transaction. It is recommended to register multiple composites at
the same time to avoid round trip overhead.
The \fItypes\fP argument is an array containing \fIcount\fP composite types
to register. Composites do not use the typput or typget members of the
PGregisterType structure, thus these memebrs are ignored. If any composite
does not exist, the register is aborted.
NOTE: The typname member of the PGregisterType structure can optionally
contain the type's schema: schema.typname.
WARNING: \fIPQparamCreate\fP is only aware of types that have already been
registered. If you need to put a composite into a param, make sure it is first
registered.
.SH EXAMPLES
.LP
.SS Using PQregisterComposites
The example registers two composite types.
.RS
.nf
.LP
\fBPGregisterType comp_types[] = {
{"myschema.simple", NULL, NULL},
{"complex", NULL, NULL}
};
if (!PQregisterComposites(conn, comp_types, 2))
fprintf(stderr, "PQregisterComposites: %s\\n", PQgeterror());
\fP
.fi
.RE
.SH RETURN VALUE
.LP
On success, a non-zero value is returned. On error, zero is
returned and \fIPQgeterror(3)\fP will contain an error message.
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP

View file

@ -0,0 +1,122 @@
.TH "PQregisterResult" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQregisterResult \- Registers sub-classes, composites and user-defined types found within a PGresult.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
int PQregisterResult(PGconn *\fIconn\fP, int \fIwhich\fP, PGregisterType *\fItypes\fP,
.br
int \fIcount\fP, PGresult *\fIres\fP);
\fP
.SH DESCRIPTION
.LP
The \fIPQregisterResult\fP() function registers the types found within
a given PGresult, thus this function makes no calls to a PostgreSQL server
since the result data is already available.
The \fIwhich\fP argument can be PQT_COMPOSITE or PQT_USERDEFINED,
but not PQT_SUBCLASS. The only reason being sub-classes don't talk to
the server so they have no result set.
The \fItypes\fP argument is an array containing \fIcount\fP types
to register. This array must be identical to what was provided to
the originating PQregisterTypes call.
The \fIres\fP argument is a PGresult normally created by calling
PQregisterTypes followed by PQgetResult. However, it is possible to
create your own result via PQmakeEmptyPGresult, PQsetResultAttrs,
PQsetvalue and call this function. This approach is a bit risky being
how the result set generated by type lookup queries are internal and
subject to change.
.SH EXAMPLES
.LP
.SS Using PQregisterResult
The example registers two composite types asynchronously. It is worth
noting that the PGresult obtained via PQgetResult can be cached by an
application and used when creating new connections, as a way to avoid
repeatedly performing type lookups with the server.
.RS
.nf
.LP
\fBPGregisterType comp_types[] = {
{"myschema.simple", NULL, NULL},
{"complex", NULL, NULL}
};
/* asynchronous registration */
if (PQregisterTypes(conn, PQT_COMPOSITE, comp_types, 2, 1))
{
/* example of a typical event loop */
for(;;)
{
int n;
fd_set set;
int fd = PQsocket(conn);
struct timeval tv = {0, 500000};
FD_ZERO(&set);
FD_SET(fd, &set);
n = select(fd + 1, &set, NULL, NULL, &tv); //or kqueue,epoll,poll,etc..
if (n == -1)
{
//error
}
else if (n == 0)
{
//timeout, do other work ....
}
else
{
PGresult *res;
PQconsumeInput(conn);
if(!PQisBusy(conn))
{
/* done */
if(!(res = PQgetResult(conn)))
break;
n = PQregisterResult(conn, PQT_COMPOSITE, comp_types, 2, res);
/* This could also be cached and reused with PQregisterResult */
PQclear(res);
if (!n)
//error, consult PQgeterror()
}
}
}
}
\fP
.fi
.RE
.SH RETURN VALUE
.LP
On success, a non-zero value is returned. On error, zero is
returned and \fIPQgeterror(3)\fP will contain an error message.
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIPQregisterTypes(3)\fP, \fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP

View file

@ -0,0 +1,62 @@
.TH "PQregisterSubClasses" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQregisterSubClasses \- Registers a type aliases or sub-classes.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQregisterSubClasses(PGconn *\fIconn\fP, PGregisterType *\fItypes\fP,
.br
int \fIcount\fP);
\fP
.SH DEPRECATED
.LP
THIS FUNCTION IS DEPRECATED. New applications should use PQregisterTypes.
This function is now a wrapper to PQregisterTypes.
.SH DESCRIPTION
.LP
The \fIPQregisterSubClasses\fP() function allows an application
to register an alias or sub-class of another type.
The \fItypes\fP argument is an array containing \fIcount\fP sub class types
to register. The typname member of the PGregisterType structure must
specify an inheritence relationship: ex. "myint=int4" where myint inherits
from int4. The \'=\' is called the inheritence operator. If both typput
and typget members of the PGregisterType structure are NULL, the type at
that element will behave identically to the type it is inheriting
from; an alias. Otherwise, the base type's put and/or get routines will
be overridden.
NOTE: The typname member of the PGregisterType structure can optionally
contain the type's schema: schema.typname.
WARNING: \fIPQparamCreate\fP is only aware of types that have already been
registered. If you need to put a type into a param, make sure it is first
registered.
.SH RETURN VALUE
.LP
On success, a non-zero value is returned. On error, zero is
returned and \fIPQgeterror(3)\fP will contain an error message.
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP

View file

@ -0,0 +1,90 @@
.TH "PQregisterTypes" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQregisterTypes \- Registers sub-classes, composites and user-defined types.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
int PQregisterTypes(PGconn *\fIconn\fP, int \fIwhich\fP, PGregisterType *\fItypes\fP,
.br
int \fIcount\fP, int \fIasync\fP);
\fP
.SH DESCRIPTION
.LP
The \fIPQregisterTypes\fP() function allows an application
to register one or more PQT_SUBCLASS, PQT_COMPOSITE or PQT_USERDEFINED
types which is defined by the \fIwhich\fP argument.
When \fIwhich\fP is PQT_COMPOSITE or PQT_USERDEFINED, this function must
execute a query against the backend to retrieve type information, thus
this should not be called from within a transaction. It is recommended to
register multiple composites at the same time to avoid round trip overhead.
The \fItypes\fP argument is an array containing \fIcount\fP types
to register. Composites do not use the typput or typget members of the
PGregisterType structure, thus these memebrs are ignored. If any composite
does not exist, the register is aborted. User-defined types must set either
typput and/or typget for each type. Sub-classes typname member of the
PGregisterType structure must specify an inheritence relationship:
ex. "myint=int4" where myint inherits from int4. The \'=\' is called the
inheritence operator. If both typput and typget members of the PGregisterType
structure are NULL, the type at that element will behave identically to the
type it is inheriting from; an alias. Otherwise, the base type's put and/or
get routines will be overridden.
The \fIasync\fP argument indicates if the asynchronous type registration
should be used. When non-zero, the caller must proceed in the normal
async libpq fashion (PQconsumeInput, PQisBusy, PQgetResult) and provide a
PGresult to PQregisterResult when obtained to complete the registration.
NOTE: The typname member of the PGregisterType structure can optionally
contain the type's schema: schema.typname.
WARNING: \fIPQparamCreate\fP is only aware of types that have already been
registered. If you need to put a type into a param, make sure it is first
registered.
.SH EXAMPLES
.LP
.SS Using PQregisterTypes
The example registers two composite types.
.RS
.nf
.LP
\fBPGregisterType comp_types[] = {
{"myschema.simple", NULL, NULL},
{"complex", NULL, NULL}
};
if (!PQregisterTypes(conn, PQT_COMPOSITE, comp_types, 2, 0))
fprintf(stderr, "PQregisterTypes: %s\\n", PQgeterror());
\fP
.fi
.RE
.SH RETURN VALUE
.LP
On success, both PQregisterTypes returns a non-zero value.
On error, zero is returned and \fIPQgeterror(3)\fP will contain an
error message.
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIPQregisterResult(3)\fP, \fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP

View file

@ -0,0 +1,82 @@
.TH "PQregisterUserDefinedTypes" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQregisterUserDefinedTypes \- Registers a user-defined types.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQregisterUserDefinedTypes(PGconn *\fIconn\fP, PGregisterType *\fItypes\fP,
.br
int \fIcount\fP);
\fP
.SH DEPRECATED
.LP
THIS FUNCTION IS DEPRECATED. New applications should use PQregisterTypes.
This function is now a wrapper to PQregisterTypes.
.SH DESCRIPTION
.LP
The \fIPQregisterUserDefinedTypes\fP() function allows an application
to register one or more user-defined types at runtime. User-defined types
are custom types in a backend that implement their own C procedures for
in/out/send/recv.
This function must execute a query against the backend to retrieve type
information for each user-defined type, thus this should not be called
from within a transaction. It is recommended to register multiple types at
the same time to avoid round trip overhead.
The \fItypes\fP argument is an array containing \fIcount\fP user-defined
types to register. If any type does not exist, the register
is aborted. Either typput and/or typget must be specified for each type
in the \fItypes\fP array.
NOTE: The typname member of the PGregisterType structure can optionally
contain the type's schema: schema.typname.
WARNING: \fIPQparamCreate\fP is only aware of types that have already been
registered. If you need to put a type into a param, make sure it is first
registered.
\fBUser-defined Types Registration\fP
.br
This example registers two user-defined types.
.nf
.RS
.LP
\fBPGregisterType types[] = {
{"graphics.rgb", rgb_put, rgb_get},
{"graphics.digon", digon_put, digon_get}
};
if (!PQregisterUserDefinedTypes(conn, types, 2))
fprintf(stderr, "PQregisterUserDefinedTypes: %s\\n", PQgeterror());
\fP
.RE
.fi
.SH RETURN VALUE
.LP
On success, a non-zero value is returned. On error, zero is
returned and \fIPQgeterror(3)\fP will contain an error message.
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP

View file

@ -0,0 +1 @@
.so man3/PQexecf.3

View file

@ -0,0 +1 @@
.so man3/PQexecf.3

View file

@ -0,0 +1 @@
.so man3/PQgeterror.3

View file

@ -0,0 +1,123 @@
.TH "PQspecPrepare" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQspecPrepare \- Prepares a libpqtypes specifier format string.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQspecPrepare(PGconn *\fIconn\fP, const char *\fIname\fP,
.br
const char *\fIformat\fP, int \fIis_stmt\fP);
.br
void PQclearSpecs(PGconn *\fIconn\fP);
\fP
.SH DESCRIPTION
.LP
PQspecPrepare allows an application to prepare specifier format strings
that will be used frequently. By preparing a specifier format string,
one avoids the parsing and type handler lookup costs. This becomes a
significant win when managing large result sets or arrays, where the
specifier format, like "%int4 %text %bytea", must be prepared for each
tuple/element.
As with PQregisterXXX, only specifier format strings prepared prior
to the creation of a PGresult or PGparam, will be available for use. This is
because the prepared type spec is cached within a PGconn object and copied
to all subsequent PGparam and PGresult objects.
Every prepared type spec is given a \fIname\fP, which is used as its unique identifier.
To use a prepared type spec, the \fIname\fP is provided where ever a regular
specifier format string is allowed, like PQputf or PQgetf. The \fIname\fP must
be proceeded by a \'@\' AT sign. For more information about the syntax,
see the \fIpqt-specs(3)\fP man page.
The \fIformat\fP argument is the specifier format string being prepared. When
this is NULL, the \fIname\fP prepared type spec is removed from the PGconn\'s
internal array.
The \fIis_stmt\fP argument indicates if a parementerized statement version of
\fIformat\fP should be cached along with the prepared type spec. This means
all type specifiers in \fIformat\fP, like "%int4", will be converted to "$1"
syntax. When is_stmt is non-zero, a statement will created and cached.
For more information on specifer format string to paremterized statements, see
the \fIPQputf(3)\fP man page. NOTE: to use a prepared type spec with
execution functions like PQexecf, \fIis_stmt\fP must be set to non-zero.
PQclearSpecs removes all prepared specifiers from the given PGconn, as
opposed to removing them one by one by setting PQspecPrepare's format
argument to NULL. A good use for this is after a PQresetXXX call when it
might be desired to re-prepare all type specifiers.
Functions that support the use of a prepared type spec are: \fIPQputf\fP,
\fIPQputvf\fP, \fIPQgetf\fP, \fIPQgetvf\fP, \fIPQexecf\fP, \fIPQexecvf\fP,
\fIPQsendf\fP, \fIPQsendvf\fP, \fIPQparamExec\fP, \fIPQparamSendQuery\fP.
\fBHINT:\fP A good rule of thumb for using prepared type specs, is when there
are a large number of PQputf/PQgetf calls per statement execution. This
commonly occurs when dealing with large result sets and arrays.
.SH RETURN VALUE
.LP
PQspecPrepare and PQclearSpecs return a nonzero value on success and
zero if it fails. Use PQgeterror() to get an error message.
.SH EXAMPLES
.LP
This example prepares a type spec and issues some PQputf calls.
.LP
.RS
.nf
\fBint i;
PQparam *param;
if(!PQspecPrepare(conn, "prepared_spec", "%int4 %text", 0))
{
fprintf(stderr, "PQspecPrepare: %s\n", PQgeterror());
exit(1);
}
/* create after preparing spec */
param = PQparamCreate(conn);
for(i=0; i < 100000; i++)
{
/* NOTE: nothing else can be in format string */
PQputf(param, "@prepared_spec", 4, "text");
}
/* This elects to prepare a statement as well. After this returns,
* "SELECT myfunc($1, $2)" will be cached along with the prepared spec.
*/
PQspecPrepare(conn, "myfunc", "SELECT myfunc(%int4, %text)", 1);
/* "myfunc" tells execf to execute "SELECT myfunc($1, $2)". If is_stmt
* was set to zero during the PQspecPrepare, the below would be invalid
* because execf doesn't know what to execute.
*/
PQexecf(conn, "@myfunc", 123, "text");
/* clear'm all */
PQclearSpecs(conn);
\fP
.fi
.RE
.SH RATIONALE
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIpqt-specs(3)\fP, \fIPQgetf(3)\fP, \fIPQputf(3)\fP.

View file

@ -0,0 +1,53 @@
.TH PQtypesRegister 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
PQtypesRegister \- Registers libpqtypes with the libpq event system.
.SH SYNOPSIS
.LP
\fB#include <libpqtypes.h>
.br
.sp
int PQtypesRegister(PGconn *conn);
\fP
.SH DEPRECATED
.LP
THIS FUNCTION IS DEPRECATED. New applications should use PQinitTypes.
This function is now a wrapper to PQinitTypes.
.SH DESCRIPTION
.LP
libpqtypes makes use of the libpq Event System. Before using
libpqtypes, you must register libpqtypes as a libpq EventProc.
The function takes a PGconn that libpqtypes will be registered
with; each PGconn requires its own registration.
.SH RETURN VALUE
.LP
The function returns zero if it fails and non-zero if it succeeds.
.SH EXAMPLES
.LP
.SS Registering libpqtypes
The examples shows how to register libpqtypes with the libpq event system.
.RS
.nf
.LP
\fB/* call prior to any other libpqtypes functions that operate on conn. */
PQtypesRegister(conn);
\fP
.fi
.RE
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
None

View file

@ -0,0 +1,263 @@
.TH "pqt-composites" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
pqt-composites \- A manual for libpqtypes composite handling.
.SH NOTE TO READER
.LP
Please read the \fIpqt-specs(3)\fP manual page prior to this document.
This document does not explain how to put or get data types. It
only describes how to put or get composites and composite arrays.
.SH DESCRIPTION
.LP
A composite is put using the PGparam structure. Each attribute of a
composite is put into a PGparam. When all attributes have been put,
the PGparam is put into another PGparam. Composites must be registered
on a per connection basis, \`man \fIpqt-handlers(3)\fP\'.
To get a composite, a PGresult structure is used. Each composite
attribute is a field of the result. For non-array composites, there
is always only one tuple.
Composites are only handled using binary format. This means that any type used
as a composite attribute must be put and gotten in binary format. If a
user-defined type does not implement a send and recv function in the backend,
it can not be used as a composite attribute.
.SS Simple Composite Example
.LP
This example demostrates the basics of putting and getting a composite type.
.nf
.RS
\fB
CREATE TYPE simple AS (a int4, t text);
PGregisterType type = {"simple", NULL, NULL};
/* need to register the simple composite */
PQregisterTypes(conn, PQT_COMPOSITE, &type, 1, 0);
/* Composite attributes are put into PGparam structures */
PGparam *simple = PQparamCreate(conn);
/* put the simple composite attributes */
PQputf(simple, "%int4 %text*", 45, "foobar");
/* Put an int4 and a simple composite */
PGparam *param = PQparamCreate(conn);
PQputf(param, "%int4 %simple", 10, simple);
PQparamClear(simple);
/* exec an insert */
res = PQparamExec(conn, param, "INSERT INTO t VALUES($1,$2)", resfmt);
PQparamClear(param);
/* -------------------------
* Getting a composite
*/
PGint4 i4;
PGtext textp;
char text[80];
PGresult *simple;
/* Get a simple composite, provide a ptr to a PGresult ptr. */
PQgetf(result, 0, "%simple", 0, &simple);
/* no longer needed */
PQclear(result);
/* Get the simple composite attributes from the simple result.
* Reference fields by name by using a '#' rather than a '%'.
* The field names are the composite attributes.
*/
PQgetf(simple, 0, "#int4 #text", "a", &i4, "t", &textp);
strcpy(text, textp);
PQclear(simple);
\fP
.RE
.nf
In the above example, we used the \'#\' specifier mark to reference
fields by their name. The field names for a composite result object
are the composite attribute names.
.SS Nested Composite example:
.LP
The below example puts and gets a nested composite. The simple composite
is used as an attribute within the complex composite.
.nf
.RS
\fB
CREATE TYPE simple AS (a int4, t text)
CREATE TYPE complex AS (f8 float8, s simple);
/* need to register simple and complex */
PGregisterType types[] = {
{"simple", NULL, NULL},
{"complex", NULL, NULL}
};
PQregisterTypes(conn, PQT_COMPOSITE, types, 2, 0);
/* Composite attributes are put into PGparam structures */
PGparam *simple = PQparamCreate(conn);
PGparam *complex = PQparamCreate(conn);
/* put the simple composite attributes */
PQputf(simple, "%int4 %text*", 45, "foobar");
/* put the complex composite attributes, which includes
* a nested composite.
*/
PQputf(complex, "%float8 %simple", 111.2223334, simple);
/* no longer needed */
PQparamClear(simple);
/* Put an int4 and a complex composite */
PGparam *param = PQparamCreate(conn);
PQputf(param, "%int4 %complex", 10, complex);
PQparamClear(complex);
/* exec an insert */
res = PQparamExec(conn, param, "INSERT INTO t VALUES($1,$2)", resfmt);
PQparamClear(param);
/* -------------------------
* Getting a nested composite
*/
PGfloat8 f8;
PGint4 i4;
PGtext textp;
char text[80];
PGresult *complex;
PGresult *simple;
/* Get the complex composite, provide a ptr to a PGresult ptr. */
PQgetf(result, 0, "%complex", 0, &complex);
/* no longer needed */
PQclear(result);
/* Get the complex composite attributes from the complex result.
* Composite attributes are the result fields. When getting
* a single composite, non-array, only tuple 0 will exist.
* For the nested simple composite, we again provide a ptr to
* a PGresult ptr.
*/
PQgetf(complex, 0, "%float8 %simple", 0, &f8, 1, &simple);
/* no longer needed */
PQclear(complex);
/* Get the simple composite attributes from the simple result.
* Reference fields by name by using a '#' rather than a '%'.
*/
PQgetf(simple, 0, "#int4 #text", "a", &i4, "t", &textp);
strcpy(text, textp);
PQclear(simple);
\fP
.RE
.nf
.SS An array of composites:
.LP
This example makes an array of complex composites. It builds
off the previous example.
.nf
.RS
\fB
int i;
PGarray complex_arr;
PGparam *simple = PQparamCreate(conn);
PGparam *complex = PQparamCreate(conn);
complex_arr.ndims = 0;
complex_arr.param = PQparamCreate(conn);
for(i=0; i < 100; i++)
{
/* put the simple composite attributes */
PQputf(simple, "%int4 %text*", 45, "foobar");
/* put the complex composite attributes, which includes
* a nested composite.
*/
PQputf(complex, "%float8 %simple", 111.2223334, simple);
/* put the complex composite */
PQputf(complex_arr.param, "%complex", complex);
/* You must reset the simple and complex composites for
* the next loop iteration.
*/
PQparamReset(simple);
PQparamReset(complex);
}
/* not needed anymore */
PQparamClear(simple);
PQparamClear(complex);
/* Put a complex composite array */
PGparam *param = PQparamCreate(conn);
PQputf(param, "%complex[]", &complex_arr);
PQparamClear(complex_arr.param);
/* exec an insert */
res = PQparamExec(conn, param, "INSERT INTO t VALUES($1)", resfmt);
PQparamClear(param);
/* -------------------------
* Getting an array of composites
*/
int i;
int ntups;
PGfloat8 f8;
PGint4 i4;
PGtext textp;
PGresult *simple;
PGarray complex_arr;
/* Get the complex[], provide a ptr to a PGarray. */
PQgetf(exec_result, 0, "%complex[]", 0, &complex_arr);
/* no longer needed */
PQclear(exec_result);
ntups = PQntuples(complex_arr.res);
for(i=0; i < ntups; i++)
{
PQgetf(complex_arr.res, i, "%float8 %simple", 0, &f8, 1, &simple);
/* Nested composites are like any other composite, tuple 0! Unless,
* its a nested composite array.
*/
PQgetf(simple, 0, "#int4 #text", "a", &i4, "t", &textp);
printf("(%f, (%d, %s))\\n", f8, i4, textp);
PQclear(simple);
}
PQclear(complex_arr.res);
\fP
.RE
.nf
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIPQgetf(3)\fP, \fIPQputf(3)\fP, \fIPQputvf(3)\fP

View file

@ -0,0 +1,598 @@
.TH "pqt-handlers" 3 2011 "libpqtypes" "libpqtypes Manual"
.SH NAME
pqt-handlers \- A manual for implementing libpqtypes type handlers.
.SH DESCRIPTION
.LP
Type handlers are I/O routines used by libpqtypes for sending and receiving
data for specific types. Internally, libpqtypes uses type handlers to support
PostgreSQL's builtin base types: such as point, int4, timestamp, etc...
NOTE: Builtin types always serialize parameters being sent "put" to the backend
in binary format; user-defined types may choose to use text format. C data
types are translated into the backend's external binary format. Even if text
results are used, C data types are still exposed when getting result data.
\fBType handlers have the below three properties:\fP
.LP
.RS
\fBType Specifier Name\fP
.br
A [schema].type name that will be used to reference your handler.
\`man \fIpqt-specs\fP\' for complete documentation on syntax.
\fBPut Routine\fP
.br
A PGtypeProc put routine takes a C data type and converts it into a valid backend
external format. The converted format is used with libpq's parameterized
API. For instance: a C int data type is used to put a postgresql int4.
To convert this to a valid external format, libpqtypes swaps the bytes
(when needed) so they are in network order. A put routine returns the number
of bytes being put. On error, a put routine must return -1.
\fBGet Routine\fP
.br
A PGtypeProc get routine does the opposite of a put routine. It converts a
type's text or binary external format to its native C type. For instance:
a postgresql int4 is converted to a C int. For binary results, the 4 bytes
are converted to host order and stored as a C int. A get routine returns zero
to indicate success. On error, a get routine must return -1.
.RE
\fBPGregisterType\fP
.br
The \fIPGregisterType\fP structure is used by all PQregisterXXX functions.
It contains a typname, put and get routine. The typname can optionally
contain the type's schema, like pg_catalog.int4. When registering a
sub class via PQregisterTypes, an inheritence operator must be used
within typname to indicate what type is being extended: myint4=int4. If the
typput and typget routines are NULL during a sub class registration, the
result is a direct sub class or alias of the base type: like "s=text"
allowing one to use "%s" instead of "%text". When registering a composite,
typput and typget are ignored.
.nf
.RS
\fB
typedef struct
{
const char *typname;
PGtypeProc typput;
PGtypeProc typget;
} PGregisterType;\fP
.RE
.fi
To implement a type handler, you need to be aware of 4 structures:
PGtypeFormatInfo, PGrecordAttDesc, PGtypeHandler and PGtypeArgs. All exist
for use with type handlers.
\fBPGtypeFormatInfo\fP
.br
The \fIPGtypeFormatInfo\fP structure provides useful connection-based information for
type handlers. For instance, your handler may have different implementations
depending on the server version .. \fIsversion\fP.
.nf
.RS
\fB
typedef struct
{
int sversion; /* server version, e.g. 70401 for 7.4.1 */
int pversion; /* FE/BE protocol version in use */
char datestyle[32]; /* server\'s datestyle: like "SQL, MDY" */
/* When non-zero, server uses int64 timestamps */
int integer_datetimes;
} PGtypeFormatInfo;\fP
.RE
.fi
\fBPGrecordAttDesc\fP
.br
The \fIPGrecordAttDesc\fP structure defines the attributes of a composite.
Internally, libpqtypes keeps track of composite attributes using this structure.
.nf
.RS
\fB
typedef struct
{
Oid attoid; /* Oid of the attribute */
int attlen; /* storage size of attribute. -1 if not known */
int atttypmod; /* The typmod of attribute. */
char *attname; /* The name of the attribute. */
} PGrecordAttDesc;\fP
.RE
.fi
\fBPGtypeHandler\fP
.br
The \fIPGtypeHandler\fP structure represents all the properties of a
type handler. When a type is registered, this structure is used to catalog
the type\'s information.
.nf
.RS
\fB
typedef struct pg_typhandler
{
/* An internal libpqtypes assigned id for this type handler. */
int id;
/* The schema name of this type, which may be empty if not
* provided during registration.
*/
char typschema[65];
/* The name of this type: like int2 or bytea, cannot be empty */
char typname[65];
/* The storage size of this type. -1 if not known. */
int typlen;
/* The backend OID of the type. */
Oid typoid;
/* The backend array OID of the type. */
Oid typoid_array;
/* The put handler for this type. */
PGtypeProc typput;
/* The get handler for this type. */
PGtypeProc typget;
/* If this handler is a sub-class, this will be the \'id\' of
* the super class type handler. It is set to -1 if not
* a sub-class.
*/
int base_id;
/* Indicates the number of composite attributes within the
* \'attDescs\' array. This is set to 0 for non-composites.
*/
int nattrs;
/* If non-zero, the 'attDescs' pointer must be freed. */
int freeAttDescs;
/* The memory behind the 'attDescs' pointer when the number of
* attrs is less than 16. When greater than 16, heap memory
* is used and 'freeAttDescs' is set to a non-zero value.
*/
PGrecordAttDesc attDescsBuf[16];
/* An array of PGrecordAttDesc, one element per record
* attribute. Must be freed if 'freeAttDescs' is non-zero.
*/
PGrecordAttDesc *attDescs;
} PGtypeHandler;
\fP
.RE
.fi
\fBPGtypeArgs\fP
.br
The \fIPGtypeArgs\fP structure is passed to all put and get handlers. It
contains all values needed by type handlers.
.nf
.RS
\fB
struct pg_typeargs
{
/* Indicates if this is a put or get operation. */
int is_put;
/* Formatting information. */
const PGtypeFormatInfo *fmtinfo;
/* Indicates if a request for a direct pointer was
* made, %text*.
*/
int is_ptr;
/*
* When \fIis_put\fP is non-zero, set this to 1 for binary and 0 for
* text format. It defaults to binary. When \fIis_put\fP is 0, this
* indicates the field type PQftype of \fIget.field_num\fP.
*/
int format;
/* An argument list. Arguments should be retrieved with va_arg. */
va_list ap;
/* The position of this typname within a specifier
* string, 1-based.
*/
int typpos;
/* Type handler for the specifier at typpos. */
PGtypeHandler *typhandler;
/*
* Report an error from within a handler. This error message
* will show up in PQgeterror.
*
* This always returns -1 so one can report an error and return
* -1 from a handler in a single statement:
*
* return args->errorf(args, "ERROR: %s", strerror(errno));
*
* errorf always prepends a small header
* "schema.typname[pos:num] - msg". For example, if the above
* failed within the int4 handler and typpos was 5, the
* resulting error message would be:
*
* pg_catalog.int4[pos:5] - ERROR: Invalid argument
*
* errorf does not put any newlines in error message.
*/
int (*errorf)(PGtypeArgs *args, const char *format, ...);
/* Used by type sub-class handlers. When \fIis_put\fP is
* non-zero, a sub-class prepares type data and then calls
* super. When \fIis_put\fP is zero, a sub-class first
* calls super to get the base class's deserialized value
* and can then convert it.
*/
int (*super)(PGtypeArgs *args, ...);
/* This structure is used when \fIis_put\fP is non-zero. */
struct
{
/* The PGparam structure passed to \fIPQputf\fP(). */
PGparam *param;
/* A buffer used to store the type's output format. If
* more than 'outl' bytes are needed, see 'expandBuffer'.
* Normally data is copied to the out buffer, but it can
* also be pointed elsewhere: like a const string or static
* memory. When repointing the out buffer, DO NOT use
* 'expandBuffer'. Never use realloc on this buffer.
*/
char *out;
/* The size in bytes of the 'out' buffer. If expandBuffer
* is used, this will reflect the new buffer length.
*/
int outl;
/* Expands the 'out' buffer to 'new_len'. If new_len is
* less than or equal to the current length 'outl', the
* expand request is ignored. This behaves just like a
* realloc, existing data is copied to the new memory.
* You should never use realloc on the out buffer.
* Returns -1 on error and 0 for success.
*/
int (*expandBuffer)(PGtypeArgs *args, int new_len);
/* internal use only. */
char *__allocated_out;
} put;
/* This structure is used when \fIis_put\fP is zero. */
struct
{
/* The PGresult passed to \fIPQgetf\fP().
PGresult *result;
/* The tuple number */
int tup_num;
/* the tuple field number. */
int field_num;
} get;
};\fP
.RE
.fi
.SH USER-DEFINED TYPES
.LP
User-defined types are extended base types in the backend. They are not domains
or composites. These types have their own input/output and send/recv functions
(normally written in C). They normally include their own operator functions and
have an array oid. For libpqtypes to make use of these types, especially for binary
puts and gets, a type handler must be registered. This provides libpqtypes with a type
specifer, put and get routines for handling this type.
User-defined types are registered on a per connection basis and must exist on the
server. If the type does not exist, the registration fails. If no schema name is
provided during registration, the server's search path is used to resolve the
type's existence and fetch its oid. If a schema name is provided during
registration, the search path is not used.
.SS User-defined type example
.LP
Assume there is a user-defined type named \'rgb\' in the \'graphics\' schema. The
text output format is always in hex: \'#ff0000\' with a leading pound sign and
lowercase hex digits. The external binary format is a sequence of three unsigned
bytes: r, g and b. To use this type with libpqtypes, it must be registered.
.nf
.RS
\fB
/* register the rgb type */
PGregisterType type = {"graphics.rgb", rgb_put, rgb_get};
PQregisterTypes(conn, PQT_USERDEFINED, &type, 1, 0);
/* put an rgb */
rgb_t rgb = {218, 218, 218};
PGparam *param = PQparamCreate(conn);
PQputf(param, "%rgb", &rgb);
/* get an rgb from tuple 0 field 4 */
rgb_t rgb;
PQgetf(result, 0, "%graphics.rgb", 4, &rgb);
/* -------------------------------
* EXAMPLE RGB IMPLEMENTATION
*/
#define hex2dec(v) (unsigned char)(((v) > '9') ? \
((v) - 'a') + 10 : (v) - '0')
/* example rgb struct */
typedef struct
{
unsigned char r;
unsigned char b;
unsigned char g;
} rgb_t;
/* RGB PGtypeProc handler - always puts in binary format */
int rgb_put(PGtypeArgs *args)
{
unsigned char *out;
rgb_t *rgb = va_arg(args->ap, rgb_t *);
/* If rgb is NULL, put an SQL NULL value */
if(!rgb)
{
args->put.out = NULL;
return 0;
}
/* write the 3 bytes to the args out buffer */
out = (unsigned char *)args->put.out;
*out++ = rgb->r;
*out++ = rgb->g;
*out = rgb->b;
return 3; /* number of bytes the server should expect */
}
/* RGB PGtypeProc handler */
int rgb_get(PGtypeArgs *args)
{
rgb_t *rgb = va_arg(args->ap, rgb_t *);
char *value = PQgetvalue(args->get.result,
args->get.tup_num, args->get.field_num);
if(!rgb)
return args->errorf(args, "rgb* cannot be NULL");
/* text format: ex. \'#ff9966\' */
if(PQfformat(args->format) == 0)
{
value++; /* skip the \'#\' sign */
rgb->r = (hex2dec(value[0]) << 4) | hex2dec(value[1]);
rgb->g = (hex2dec(value[2]) << 4) | hex2dec(value[3]);
rgb->b = (hex2dec(value[4]) << 4) | hex2dec(value[5]);
return 0;
}
/* binary format */
rgb->r = (unsigned char)value[0];
rgb->g = (unsigned char)value[1];
rgb->b = (unsigned char)value[2];
return 0;
}\fP
.RE
.fi
.SH TYPE SUB-CLASSING
.LP
Sub-classing a type means extending the put or get routines of a registered
type handler. The idea came about from trying to provide a convention for
registering domains; which amounts to simple aliases to libpqtypes.
Domain/alias registration would look like this:
.nf
.RS
\fBPGregisterType type = {"myint4=pg_catalog.int4", NULL, NULL};
PQregisterTypes(conn, PQT_SUBCLASS, &type, 1, 0);\fP
.RE
.fi
The 'typname' member syntax is: [schema].type=[base_schema].base_type
(schema is optional). No spaces are allowed unless contained within the schema
or type name, which would require double quoting the identifer. By passing NULL
for both the put and get handlers, the base type's handlers are used. Thus,
the result of the above is that "%myint4" and "%int4" behave identically. But
what happens if a put or get handler is provided during an alias registration?
Is this useful functionality to applications? The answer is sub-classing and
yes its useful.
By providing a put and get handler during alias registration, one has
effectively sub-classed the base type. This is called sub-class registration.
By sub-classing a registered type, applications can now put and get data
using their own data structures. The sub-class put and get routines handle
the dirty work of converting application structures to the base type's
structure. When sub-classing, no oid lookup occurs with the server. The
sub-class type is assumed to be application specific. Sub-classes are
registered on a per connection basis, just like user-defined types. The
reason for this is because the base type can be server-specific.
\fBBENEFITS\fP
1. Centralizes conversions from application data types to libpq data types
.br
2. Provides an easy all-inclusive interface for putting and getting values
.br
3. Allows applications to piggy-back off libpqtypes internal binary and text convertors
.br
4. Adds enormous flexiblity: (a few interesting ideas)
.br
-- %socket: sub-class the inet get routine and return a connected sockfd.
.br
-- %file: sub-class the text get routine and return a FILE* (text being a pathname)
.br
-- %filemd5: sub-class the bytea put routine and supply a pathname that is used to
md5 a file's contents, utlimately putting a 16 byte bytea.
It is impossible to consider all of the uses for type sub-classing. The above
ideas are probably more extreme than common cases, such as taking an application
struct and converting it to what the base type expects. But, the extreme cases
are possible when desired.
.SS Sub-class example
.LP
Assume you have an application that works with time_t epoch values a lot. It
would be useful if you could define a %epoch type handler. This avoids having
to convert a time_t to either a string or to a PGtimestamp (used by the timestamp &
timestamptz type handlers). The problem is, to use the binary interface you would
have to know how to serialize a timestamp to send/recv it from the server. If you
sub-class timestamptz, you can use PGtypeArgs.super to handle the dirty work.
\fB**NOTE:\fP %epoch is only an example, it is not part of libpqtypes nor being proposed.
The goal here is to demonstrate how to implement a type sub-class handler. It is important
to note that %epoch will announce itself as a timestamptz to the backend. So when using
%epoch, make sure the context allows a timestamptz.
.nf
.RS
\fB
/* we are going to register this under the \'pqt\' schema */
PGregisterType type = {"pqt.epoch=pg_catalog.timestamptz", epoch_put, epoch_get};
PQregisterTypes(conn, PQT_SUBCLASS, &type, 1, 0))
/* putting an epoch */
struct stat st;
if(stat("/home/foobar/archive.tgz", &st) == 0)
{
PGparam *param = PQparamCreate(conn);
PQputf(param, "%epoch", st.st_mtime);
//....
}
/* getting an epoch value, using fully qualified type name */
struct utimbuf ut = {0, 0};
PQgetf(result, tup_num, "%pqt.epoch", field_num, &ut.modtime);
/* -------------------------------
* EXAMPLE EPOCH SUB-CLASS IMPLEMENTATION
*/
/* convert a time_t to a PGtimestamp and call args->super() */
int epoch_put(PGtypeArgs *args)
{
struct tm *tm;
PGtimestamp ts;
time_t t = va_arg(args->ap, time_t);
tm = localtime(&t);
ts.date.isbc = 0;
ts.date.year = tm->tm_year + 1900; /* always 4-digit year */
ts.date.mon = tm->tm_mon;
ts.date.mday = tm->tm_mday;
ts.time.hour = tm->tm_hour;
ts.time.min = tm->tm_min;
ts.time.sec = tm->tm_sec;
ts.time.usec = 0;
ts.time.gmtoff = tm->tm_gmtoff;
/* Internally, this calls the base type\'s put routine
* (the super class). In this case, the super class
* expects a PGtimestamp as input. The super function
* returns whatever the base type\'s put routine returns
* (which for all puts is the byte count or -1 on error).
*/
return args->super(args, &ts);
}
/* Calls args->super() to get a PGtimestamp and then converts
* it to a time_t value.
*/
int epoch_get(PGtypeArgs *args)
{
PGtimestamp ts;
time_t *t = va_arg(args->ap, time_t *);
if(!t)
return args->errorf(args, "time_t* cannot be NULL");
/* zero user bits */
*t = 0;
/* Internally, this calls the base type\'s get routine,
* which returns 0 or -1 on error.
*/
if(args->super(args, &ts) == -1)
return -1; /* args->errorf called by super already */
/* Since PGtimestamp contains an epoch member, we can
* just copy that value rather than calling mktime().
*/
*t = (time_t)ts.epoch;
return 0;
}
\fP
.RE
.fi
.SH COMPOSITES
.LP
To get and put composites, they must be registered. During registration,
information about the composite type, likes its OID and attributes, are looked
up in the backend. The composite must exist or the registration fails.
Do a \`man \fIpqt-composites(3)\fP\' for a more information about composites.
Registering a composite type:
.nf
.RS
\fB
CREATE TYPE simple AS (a int4, t text);
PGregisterType type = {"simple", NULL, NULL};
PQregisterTypes(conn, PQT_COMPOSITE, &type, 1, 0);\fP
.RE
.fi
*) The put and get routines must be NULL, composites cannot be sub-classed
.br
*) The provided name cannot resolve to the backend\'s RECORDOID
.br
*) The composite must exist at "conn"
.br
*) If no schema name is provided, the composite must be within the backend\'s search path.
During registration of a composite, the below information is retreived from the backend:
*) Oid of the composite type
.br
*) Array Oid of the composite type
.br
*) Type len of the compsoite type, PQfsize
For each composite attribute:
*) Oid of the attribute
.br
*) Name of the attribute
.br
*) Type len of the attribute, PQfsize
.br
*) The typmod of the attribute, PQfmod
.SH EXAMPLES
.LP
None.
.SH AUTHOR
.LP
A contribution of eSilo, LLC. for the PostgreSQL Database Management System.
Written by Andrew Chernow and Merlin Moncure.
.SH REPORTING BUGS
.LP
Report bugs to <libpqtypes@esilo.com>.
.SH COPYRIGHT
.LP
Copyright (c) 2011 eSilo, LLC. All rights reserved.
.br
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.LP
\fIPQregisterTypes\fP(), \fIPQregisterResult\fP()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,109 @@
#!/bin/bash
########################################################################
# Converts a man page to very simple html, basically a page with
# A and PRE tags. Takes 1 arguments: path to the non-compressed man
# page. This expects the man page to be named PAGE_NAME.SECTION_NUMBER;
# like `PQgetf.3' or `printf.1'.
#
# FEATURES
# 1. Any some_man(3), no spaces before '(', is substituted for an
# A tag linking to http://libpqtypes.esilo.com/man3/$man.html
#
# 2. All '<' and '>' are replaced with HTML codes
#
# 3. Mans referencing other mans, via .so macro, are converted
# to symlinks in the output directory.
#
# ISSUES
# Hyperlinks are not 100% substitued into resulting html page.
# The only time there are issues is when the link text in the
# man page was broken across lines. No work-around at this time
# for this issue other than manually ensuring line breaks don't
# occur on `some_man(3)'.
#
# Man pages that reference other man pages, using the .so macro,
# must have the .so command in the first 'head' lines. The
# .so 'path_to_man' cannot include spaces.
######################################################################
DOCTYPE="<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
# adjust these to your web server
BASEURL="http:\/\/libpqtypes.esilo.com"
INC_BASEURL="$BASEURL\/include"
MAN_BASEURL="$BASEURL\/man"
OUTDIR=/esilo/www/libpqtypes.esilo.com/man
HEAD_SECTION="<link rel=\"stylesheet\" href=\"/libpqtypes.css\" type=\"text/css\">"
HEADER="<a href='/'>libpqtypes home page</a><p>"
FOOTER="<p><a href='/'>libpqtypes home page</a>"
if [ $# = 0 ] ; then
echo "Must supply man page to convert"
exit
fi
# ignore non-existent files
if test ! -f $1 ; then
exit
fi
manpage=`basename $1`
section=`echo "$manpage" | awk -F . '{print $NF}'`
manpage=${manpage%.[^.]*}
OUTDIR="${OUTDIR}${section}"
mkdir -p $OUTDIR
# If a .so reference, create a symlink
solink=`head $1 | grep '.so .*' | cut -d ' ' -f 2`
if test ! -z $solink ; then
target=`basename $solink`
target=${target%.[^.]*}
echo "$OUTDIR/$manpage.html => $OUTDIR/$target.html"
# remove existing symlink
rm -f $OUTDIR/$manpage.html
ln -s $OUTDIR/$target.html $OUTDIR/$manpage.html
exit
fi
echo $OUTDIR/$manpage.html
>$OUTDIR/$manpage.html
# doctype
if test ! -z "$DOCTYPE" ; then
echo $DOCTYPE >>$OUTDIR/$manpage.html
fi
# add stadard html tags, include a title
echo -e "<html>\n<head>\n$HEAD_SECTION\n<title>man $manpage</title>\n</head>\n<body>" >>$OUTDIR/$manpage.html
if test ! -z "$HEADER" ; then
echo $HEADER >>$OUTDIR/$manpage.html
fi
echo "<pre>" >>$OUTDIR/$manpage.html
# 1. output man in ascii
# 2. post-process with col removing backspaces and tabs
# 3. do some html code replacement
# 4. replace some_man(3) with A tags
groff -t -e -mandoc -Tascii $1 2>/dev/null | col -bx | \
sed 's/</\&lt;/g' | sed 's/>/\&gt;/g' | \
sed "s/\&lt;\([a-ZA-Z0-9_\-]*\.h\)\&gt;/\&lt;<a href='$BASEURL\/browse_source\.html\?file=\1'>\1<\/a>\&gt;/g" | \
sed "s/\b\([a-zA-Z0-9_\-]\+\)(\([0-9]\))/<a href='$MAN_BASEURL\2\/\1\.html'>\1(\2)<\/a>/g" \
>>$OUTDIR/$manpage.html
echo -e "</pre>\n" >>$OUTDIR/$manpage.html
if test ! -z "$FOOTER" ; then
echo $FOOTER >>$OUTDIR/$manpage.html
fi
# html closing tags
echo -e "</body>\n</html>" >>$OUTDIR/$manpage.html
chown apache:apache $OUTDIR/$manpage.html

View file

@ -0,0 +1,323 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2005-05-14.22
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
chmodcmd="$chmodprog 0755"
chowncmd=
chgrpcmd=
stripcmd=
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=
dst=
dir_arg=
dstarg=
no_target_directory=
usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
-c (ignored)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
--help display this help and exit.
--version display version info and exit.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
"
while test -n "$1"; do
case $1 in
-c) shift
continue;;
-d) dir_arg=true
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
--help) echo "$usage"; exit $?;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-s) stripcmd=$stripprog
shift
continue;;
-t) dstarg=$2
shift
shift
continue;;
-T) no_target_directory=true
shift
continue;;
--version) echo "$0 $scriptversion"; exit $?;;
*) # When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
test -n "$dir_arg$dstarg" && break
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dstarg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dstarg"
shift # fnord
fi
shift # arg
dstarg=$arg
done
break;;
esac
done
if test -z "$1"; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call `install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
for src
do
# Protect names starting with `-'.
case $src in
-*) src=./$src ;;
esac
if test -n "$dir_arg"; then
dst=$src
src=
if test -d "$dst"; then
mkdircmd=:
chmodcmd=
else
mkdircmd=$mkdirprog
fi
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dstarg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dstarg
# Protect names starting with `-'.
case $dst in
-*) dst=./$dst ;;
esac
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
if test -n "$no_target_directory"; then
echo "$0: $dstarg: Is a directory" >&2
exit 1
fi
dst=$dst/`basename "$src"`
fi
fi
# This sed command emulates the dirname command.
dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# Skip lots of stat calls in the usual case.
if test ! -d "$dstdir"; then
defaultIFS='
'
IFS="${IFS-$defaultIFS}"
oIFS=$IFS
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
shift
IFS=$oIFS
pathcomp=
while test $# -ne 0 ; do
pathcomp=$pathcomp$1
shift
if test ! -d "$pathcomp"; then
$mkdirprog "$pathcomp"
# mkdir can fail with a `File exist' error in case several
# install-sh are creating the directory concurrently. This
# is OK.
test -d "$pathcomp" || exit
fi
pathcomp=$pathcomp/
done
fi
if test -n "$dir_arg"; then
$doit $mkdircmd "$dst" \
&& { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
&& { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
&& { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
else
dstfile=`basename "$dst"`
# Make a couple of temp file names in the proper directory.
dsttmp=$dstdir/_inst.$$_
rmtmp=$dstdir/_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
trap '(exit $?); exit' 1 2 13 15
# Copy the file name to the temp name.
$doit $cpprog "$src" "$dsttmp" &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
&& { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
&& { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
# Now rename the file to the real destination.
{ $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
|| {
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
if test -f "$dstdir/$dstfile"; then
$doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
|| $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
|| {
echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
(exit 1); exit 1
}
else
:
fi
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
}
}
fi || { (exit 1); exit 1; }
done
# The final little trick to "correctly" pass the exit status to the exit trap.
{
(exit 0); exit 0
}
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-end: "$"
# End:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,360 @@
#! /bin/sh
# Common stub for a few missing GNU programs while installing.
scriptversion=2005-06-08.21
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
fi
run=:
# In the cases where this matters, `missing' is being run in the
# srcdir already.
if test -f configure.ac; then
configure_ac=configure.ac
else
configure_ac=configure.in
fi
msg="missing on your system"
case "$1" in
--run)
# Try to run requested program, and just exit if it succeeds.
run=
shift
"$@" && exit 0
# Exit code 63 means version mismatch. This often happens
# when the user try to use an ancient version of a tool on
# a file that requires a minimum version. In this case we
# we should proceed has if the program had been absent, or
# if --run hadn't been passed.
if test $? = 63; then
run=:
msg="probably too old"
fi
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
error status if there is no known handling for PROGRAM.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
--run try to run the given command, and emulate it if it fails
Supported PROGRAM values:
aclocal touch file \`aclocal.m4'
autoconf touch file \`configure'
autoheader touch file \`config.h.in'
automake touch all \`Makefile.in' files
bison create \`y.tab.[ch]', if possible, from existing .[ch]
flex create \`lex.yy.c', if possible, from existing .c
help2man touch the output file
lex create \`lex.yy.c', if possible, from existing .c
makeinfo touch the output file
tar try tar, gnutar, gtar, then tar without non-portable flags
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: Unknown \`$1' option"
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
;;
esac
# Now exit if we have it, but it failed. Also exit now if we
# don't have it and --version was passed (most likely to detect
# the program).
case "$1" in
lex|yacc)
# Not GNU programs, they don't have --version.
;;
tar)
if test -n "$run"; then
echo 1>&2 "ERROR: \`tar' requires --run"
exit 1
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
exit 1
fi
;;
*)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
# Could not run --version or --help. This is probably someone
# running `$TOOL --version' or `$TOOL --help' to check whether
# $TOOL exists and not knowing $TOOL uses missing.
exit 1
fi
;;
esac
# If it does not exist, or fails to run (possibly an outdated version),
# try to emulate it.
case "$1" in
aclocal*)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
to install the \`Automake' and \`Perl' packages. Grab them from
any GNU archive site."
touch aclocal.m4
;;
autoconf)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`${configure_ac}'. You might want to install the
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
archive site."
touch configure
;;
autoheader)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`acconfig.h' or \`${configure_ac}'. You might want
to install the \`Autoconf' and \`GNU m4' packages. Grab them
from any GNU archive site."
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
test -z "$files" && files="config.h"
touch_files=
for f in $files; do
case "$f" in
*:*) touch_files="$touch_files "`echo "$f" |
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
*) touch_files="$touch_files $f.in";;
esac
done
touch $touch_files
;;
automake*)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
You might want to install the \`Automake' and \`Perl' packages.
Grab them from any GNU archive site."
find . -type f -name Makefile.am -print |
sed 's/\.am$/.in/' |
while read f; do touch "$f"; done
;;
autom4te)
echo 1>&2 "\
WARNING: \`$1' is needed, but is $msg.
You might have modified some files without having the
proper tools for further handling them.
You can get \`$1' as part of \`Autoconf' from any GNU
archive site."
file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
if test -f "$file"; then
touch $file
else
test -z "$file" || exec >$file
echo "#! /bin/sh"
echo "# Created by GNU Automake missing as a replacement of"
echo "# $ $@"
echo "exit 0"
chmod +x $file
exit 1
fi
;;
bison|yacc)
echo 1>&2 "\
WARNING: \`$1' $msg. You should only need it if
you modified a \`.y' file. You may need the \`Bison' package
in order for those modifications to take effect. You can get
\`Bison' from any GNU archive site."
rm -f y.tab.c y.tab.h
if [ $# -ne 1 ]; then
eval LASTARG="\${$#}"
case "$LASTARG" in
*.y)
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" y.tab.c
fi
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" y.tab.h
fi
;;
esac
fi
if [ ! -f y.tab.h ]; then
echo >y.tab.h
fi
if [ ! -f y.tab.c ]; then
echo 'main() { return 0; }' >y.tab.c
fi
;;
lex|flex)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified a \`.l' file. You may need the \`Flex' package
in order for those modifications to take effect. You can get
\`Flex' from any GNU archive site."
rm -f lex.yy.c
if [ $# -ne 1 ]; then
eval LASTARG="\${$#}"
case "$LASTARG" in
*.l)
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" lex.yy.c
fi
;;
esac
fi
if [ ! -f lex.yy.c ]; then
echo 'main() { return 0; }' >lex.yy.c
fi
;;
help2man)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified a dependency of a manual page. You may need the
\`Help2man' package in order for those modifications to take
effect. You can get \`Help2man' from any GNU archive site."
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
if test -z "$file"; then
file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
fi
if [ -f "$file" ]; then
touch $file
else
test -z "$file" || exec >$file
echo ".ab help2man is required to generate this page"
exit 1
fi
;;
makeinfo)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified a \`.texi' or \`.texinfo' file, or any other file
indirectly affecting the aspect of the manual. The spurious
call might also be the consequence of using a buggy \`make' (AIX,
DU, IRIX). You might want to install the \`Texinfo' package or
the \`GNU make' package. Grab either from any GNU archive site."
# The file to touch is that specified with -o ...
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
if test -z "$file"; then
# ... or it is the one specified with @setfilename ...
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile`
# ... or it is derived from the source name (dir/f.texi becomes f.info)
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
fi
# If the file does not exist, the user really needs makeinfo;
# let's fail without touching anything.
test -f $file || exit 1
touch $file
;;
tar)
shift
# We have already tried tar in the generic part.
# Look for gnutar/gtar before invocation to avoid ugly error
# messages.
if (gnutar --version > /dev/null 2>&1); then
gnutar "$@" && exit 0
fi
if (gtar --version > /dev/null 2>&1); then
gtar "$@" && exit 0
fi
firstarg="$1"
if shift; then
case "$firstarg" in
*o*)
firstarg=`echo "$firstarg" | sed s/o//`
tar "$firstarg" "$@" && exit 0
;;
esac
case "$firstarg" in
*h*)
firstarg=`echo "$firstarg" | sed s/h//`
tar "$firstarg" "$@" && exit 0
;;
esac
fi
echo 1>&2 "\
WARNING: I can't seem to be able to run \`tar' with the given arguments.
You may want to install GNU tar or Free paxutils, or check the
command line arguments."
exit 1
;;
*)
echo 1>&2 "\
WARNING: \`$1' is needed, and is $msg.
You might have modified some files without having the
proper tools for further handling them. Check the \`README' file,
it often tells you about the needed prerequisites for installing
this package. You may also peek at any GNU archive site, in case
some other package would contain this missing \`$1' program."
exit 1
;;
esac
exit 0
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-end: "$"
# End:

View file

@ -0,0 +1,278 @@
/*
* array.c
* Type handler for the array data type.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
int
pqt_put_array(PGtypeArgs *args)
{
int i;
int hasnull=0;
int ndims;
int nitems;
int arrsize;
char *out;
int lbound[MAXDIM];
int dims[MAXDIM];
PGarray *arr = va_arg(args->ap, PGarray *);
PUTNULLCHK(args, arr);
if (arr->ndims < 0)
return args->errorf(args, "arr.ndims is invalid - %d", arr->ndims);
/* auto configure when ndims is 0 to 1d array */
if (arr->ndims == 0)
{
ndims = 1;
dims[0] = arr->param->vcnt;
lbound[0] = 1;
}
else
{
ndims = arr->ndims;
memcpy(lbound, arr->lbound, sizeof(lbound));
memcpy(dims, arr->dims, sizeof(dims));
}
nitems = 1;
for (i=0; i < ndims; i++)
nitems *= dims[i];
/* make sure array is on the same page as the param */
if (nitems != arr->param->vcnt)
return args->errorf(args,
"param element count %d is different than array's %d",
arr->param->vcnt, nitems);
/* header: ndims + hasnull + elemtype + ((dims + lbound) * ndims) */
arrsize = 4 + 4 + 4 + (8 * ndims);
/* compute data length, also get the hasnull flag */
for (i=0; i < arr->param->vcnt; i++)
{
if (arr->param->vals[i].format == 0)
return args->errorf(args, "Cannot put array elements in text format");
arrsize += 4;
if (arr->param->vals[i].datal == NULL_LEN)
hasnull = 1;
else
arrsize += arr->param->vals[i].datal;
}
/* make sure args->put.out is large enough */
if (args->put.expandBuffer(args, arrsize) == -1)
return -1;
out = args->put.out;
/* number od dimensions */
pqt_buf_putint4(out, ndims);
out += 4;
/* array hasnull flag */
pqt_buf_putint4(out, hasnull);
out += 4;
/* array element oid */
pqt_buf_putint4(out, args->typhandler->typoid);
out += 4;
/* dims and lbound */
for (i=0; i < ndims; i++)
{
pqt_buf_putint4(out, dims[i]);
out += 4;
pqt_buf_putint4(out, lbound[i]);
out += 4;
}
/* write the element lengths and data */
for (i=0; i < arr->param->vcnt; i++)
{
pqt_buf_putint4(out, arr->param->vals[i].datal);
out += 4;
if (arr->param->vals[i].datal > 0)
{
memcpy(out, arr->param->vals[i].data, arr->param->vals[i].datal);
out += arr->param->vals[i].datal;
}
}
return arrsize;
}
int
pqt_get_array(PGtypeArgs *args)
{
int i,t;
int vlen;
int ntups;
int nattrs;
Oid elemoid;
DECLVALUE(args);
PGresult *res;
int first_tup;
PGarray *arr = va_arg(args->ap, PGarray *);
CHKGETVALS(args, arr);
if (args->format == TEXTFMT)
return args->errorf(args, "array does not support text results");
/* number of dims */
arr->ndims = pqt_buf_getint4(value);
value += 4;
/* skip NULL flag */
value += 4;
/* check the element oid */
elemoid = (Oid)pqt_buf_getint4(value);
if (elemoid != args->typhandler->typoid)
return args->errorf(args,
"array element type %u is different than what server says %u",
args->typhandler->typoid, elemoid);
value += 4;
/* arr dims and lbounds */
first_tup = 1;
for (i=0, ntups=1; i < arr->ndims; i++)
{
arr->dims[i] = pqt_buf_getint4(value);
value += 4;
arr->lbound[i] = pqt_buf_getint4(value);
value += 4;
ntups *= arr->dims[i];
}
/* This means ndims is zero because the above loop never iterated. */
if (i == 0)
ntups = 0;
/* numTuples is the number of array items
* and numAttributes is 1 for non-composites.
*/
nattrs = (args->typhandler->nattrs > 0) ? args->typhandler->nattrs : 1;
if (!(res = pqt_copyresult(args, nattrs)))
RERR_MEM(args);
for (t=0; t < ntups; t++)
{
/* get the value len */
vlen = pqt_buf_getint4(value);
value += 4;
/* Regular Array with 1 attr per tuple */
if (args->typhandler->nattrs == 0)
{
/* set the field value */
if (!PQsetvalue(res, t, 0, value, vlen))
{
PQclear(res);
return -1;
}
if (vlen > 0)
value += vlen;
continue;
}
/* ------------------------
* COMPOSITE/RECORD
*/
/* NULL compsoite array item, fill in attrs with NULL */
if (vlen == NULL_LEN)
{
int x;
for (x=0; x < nattrs; x++)
{
if (!PQsetvalue(res, t, x, NULL, NULL_LEN))
{
PQclear(res);
return -1;
}
}
/* move on to next tuple, done here. */
continue;
}
/* verify that server's attr count matches ours */
if (first_tup)
{
int attcnt = pqt_buf_getint4(value);
/* watch for invalidation issues */
if (attcnt != nattrs)
{
PQclear(res);
return args->errorf(args,
"type handler attribute count is %d but server says it's %d",
args->typhandler->nattrs, attcnt);
}
}
/* skip attr count */
value += 4;
/* composite attributes (record columns) */
for (i=0; i < nattrs; i++)
{
/* watch for invalidation issues */
if (first_tup &&
(Oid) pqt_buf_getint4(value) != args->typhandler->attDescs[i].attoid)
{
Oid server_oid = (Oid) pqt_buf_getint4(value);
args->errorf(args,
"type handler attribute OID is %u but server says it's %u",
args->typhandler->attDescs[i].attoid, server_oid);
PQclear(res);
return -1;
}
/* skip oid */
value += 4;
/* get the value length */
vlen = pqt_buf_getint4(value);
value += 4;
/* set the field value */
if (!PQsetvalue(res, t, i, value, vlen))
{
PQclear(res);
return -1;
}
if (vlen > 0)
value += vlen;
}
first_tup = 0;
}
arr->res = res;
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,311 @@
/*
* error.c
* The functions in this file represent the libpqtypes error
* system. It offers the API user the ability to set/get errors.
* The error system uses a per-thread global error, implemented
* using TLS (via declspec(thread) or pthread TLS). For
* non-PQT_THREAD_SAFE builds, a static buffer is used.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
/* Cannot use allocated memory for the pqterr_t members. This is
* because windows has no way of freeing per thread allocated
* memory automatically, other than DllMain which won't work for
* a third-party library such as libpqtypes. All error messages
* and fields are truncated to fit their buffers via pqt_strcpy().
*/
typedef struct
{
/* Room for user message and a full libpq message */
char msg[1024 + 8192];
/* Error Fields (a little less than 8K for all) */
char severity[16];
char sqlstate[16];
char message_primary[2048];
char message_detail[1024];
char message_hint[512];
char stmt_position[16];
char internal_position[16];
char internal_query[2048];
char context[2048];
char source_file[256];
char source_line[16];
char source_function[80];
} pqterr_t;
/* non thread-safe, use a static */
#ifndef PQT_THREAD_SAFE
static pqterr_t lasterr = {{0}};
/* MSVC always uses __declspec(thread) */
#elif defined(PQT_MSVC)
static __declspec(thread) pqterr_t lasterr = {0};
/* Unix-variant, MinGW or Cygwin. */
#else
#include <pthread.h>
static pthread_key_t tlskey_lasterr;
/* When the thread dies, this will get called. */
static void tls_free_lasterr(void *value)
{
if (value)
{
free(value);
pthread_setspecific(tlskey_lasterr, NULL);
}
}
/* called before main. This attribute is available since gcc 2.7.0.
* It replaced the obsolete _init ... destructor replaced _fini.
* We used to use pthread_once, but solaris doesn't support this
* in older versions (its simply a stub returning 0, ouch!).
*/
void __attribute__((constructor)) __InItErRoRkEy__(void)
{
pthread_key_create(&tlskey_lasterr, tls_free_lasterr);
}
#endif
static void
vseterror(const char *format, va_list ap, int append);
static pqterr_t *
geterr(void)
{
pqterr_t *err = NULL;
/* Non thread-safe mode or windows msvc. */
#if !defined(PQT_THREAD_SAFE) || defined(PQT_MSVC)
err = &lasterr;
/* systems requiring pthread TLS keys */
#else
/* System is using FSU Threads, like SCO OpenServer 5, which
* has minor prototype differences.
*/
# ifdef PTHREAD_FSU
pthread_getspecific(tlskey_lasterr, (void **) &err);
# else
err = (pqterr_t *) pthread_getspecific(tlskey_lasterr);
# endif
if (!err)
{
err = (pqterr_t *) malloc(sizeof(pqterr_t));
if (!err)
return NULL;
memset(err, 0, sizeof(pqterr_t));
pthread_setspecific(tlskey_lasterr, err);
}
#endif
return err;
}
char *
PQgeterror(void)
{
static char _empty[1] = {0};
pqterr_t *err = geterr();
return err ? err->msg : _empty;
}
void
PQseterror(const char *format, ...)
{
/* clear error message by passing in NULL, PQseterror(NULL).*/
if (!format)
{
pqterr_t *err = geterr();
if (err)
memset(err, 0, sizeof(pqterr_t));
}
else
{
va_list ap;
va_start(ap, format);
vseterror(format, ap, FALSE);
}
}
char *
PQgetErrorField(int fieldcode)
{
pqterr_t *err = geterr();
if (!err)
return NULL;
switch (fieldcode)
{
case PG_DIAG_SEVERITY:
return err->severity;
case PG_DIAG_SQLSTATE:
return err->sqlstate;
case PG_DIAG_MESSAGE_PRIMARY:
return err->message_primary;
case PG_DIAG_MESSAGE_DETAIL:
return err->message_detail;
case PG_DIAG_MESSAGE_HINT:
return err->message_hint;
case PG_DIAG_STATEMENT_POSITION:
return err->stmt_position;
case PG_DIAG_INTERNAL_POSITION:
return err->internal_position;
case PG_DIAG_INTERNAL_QUERY:
return err->internal_query;
case PG_DIAG_CONTEXT:
return err->context;
case PG_DIAG_SOURCE_FILE:
return err->source_file;
case PG_DIAG_SOURCE_LINE:
return err->source_line;
case PG_DIAG_SOURCE_FUNCTION:
return err->source_function;
default:
return NULL;
}
}
/* Used by pqt_setresultfields */
#define geterrfield(buf, name) do{ \
if ((value = PQresultErrorField(res, name))) \
pqt_strcpy(buf, sizeof(buf), value); \
else \
*buf = 0; \
}while (0)
void
pqt_setresultfields(const PGresult *res)
{
char *value;
pqterr_t *err = geterr();
if (!err)
return;
geterrfield(err->severity, PG_DIAG_SEVERITY);
geterrfield(err->sqlstate, PG_DIAG_SQLSTATE);
geterrfield(err->message_primary, PG_DIAG_MESSAGE_PRIMARY);
geterrfield(err->message_detail, PG_DIAG_MESSAGE_DETAIL);
geterrfield(err->message_hint, PG_DIAG_MESSAGE_HINT);
geterrfield(err->stmt_position, PG_DIAG_STATEMENT_POSITION);
geterrfield(err->internal_position, PG_DIAG_INTERNAL_POSITION);
geterrfield(err->internal_query, PG_DIAG_INTERNAL_QUERY);
geterrfield(err->context, PG_DIAG_CONTEXT);
geterrfield(err->source_file, PG_DIAG_SOURCE_FILE);
geterrfield(err->source_line, PG_DIAG_SOURCE_LINE);
geterrfield(err->source_function, PG_DIAG_SOURCE_FUNCTION);
}
/* errorf() callback for PGtypeArgs, see PQputf() and PQgetf().
* Always returns -1.
*/
int
pqt_argserrorf(PGtypeArgs *args, const char *format, ...)
{
va_list ap;
char fqtn[200];
if (!args || !format || !*format)
return -1;
pqt_fqtn(fqtn, sizeof(fqtn), args->typhandler->typschema,
args->typhandler->typname);
/* put the header */
PQseterror("%s[pos:%d] - ", fqtn, args->typpos);
/* append message from type handler */
va_start(ap, format);
vseterror(format, ap, TRUE);
return -1;
}
static void
vseterror(const char *format, va_list ap, int append)
{
int n;
int curlen = 0;
int size;
va_list ap2;
char *msg = NULL;
pqterr_t *err = geterr();
if (!err)
return;
if (append)
curlen = (int) strlen(err->msg);
else
*err->msg = 0;
va_copy(ap2, ap);
n = pqt_vsnprintf(err->msg + curlen, sizeof(err->msg) - curlen, format, ap2);
va_end(ap2);
if (n > -1)
return;
/* pqterr_t msg buffer is too small for the complete message. We have
* use a temporary buffer to get a successful sprintf so we can
* pqt_strcpy() the result; which truncates to fit.
*/
size = (int) sizeof(err->msg) * 2;
if (!(msg = (char *) malloc(size)))
return;
while (1)
{
char *p;
va_copy(ap2, ap);
n = pqt_vsnprintf(msg + curlen, size - curlen, format, ap2);
va_end(ap2);
/* success */
if (n > -1)
break;
/* need more space */
n = size * 2;
if (!(p = pqt_realloc(msg, n)))
{
/* we're here because sprintf failed, don't trust buffer contents */
*msg = 0;
break;
}
msg = p;
size = n;
}
pqt_strcpy(err->msg, sizeof(err->msg), msg);
free(msg);
}

View file

@ -0,0 +1,173 @@
/*
* events.c
* The libpq PGEventProc implementation.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
static PGtypeData *
allocTypeData(PGconn *conn);
static void
freeTypeData(PGtypeData *typeData);
/* Deprecated, use PQinitTypes instead */
int
PQtypesRegister(PGconn *conn)
{
return PQinitTypes(conn);
}
int
pqt_eventproc(PGEventId id, void *info, void *passThrough)
{
switch (id)
{
case PGEVT_REGISTER:
{
PGEventRegister *e = (PGEventRegister *) info;
void *data = allocTypeData(e->conn);
if (!data)
return FALSE;
PQsetInstanceData((PGconn *) e->conn, pqt_eventproc, data);
break;
}
case PGEVT_CONNRESET:
{
/* No special handling for PGEVT_CONNRESET. Previously, types were
* automatically re-registered but this idea fails miserably during
* asynchronous resets. Yanked in favor of using the following
* call sequence: PQresetXXX, PQclearTypes, PQregisterTypes.
*/
break;
}
case PGEVT_CONNDESTROY:
{
PGEventConnDestroy *e = (PGEventConnDestroy *) info;
freeTypeData((PGtypeData *) PQinstanceData(e->conn, pqt_eventproc));
break;
}
case PGEVT_RESULTCREATE:
{
PGtypeData *resData;
PGEventResultCreate *e = (PGEventResultCreate *) info;
PGtypeData *connData = (PGtypeData *) PQinstanceData(
e->conn, pqt_eventproc);
if (!connData || !(resData = allocTypeData(e->conn)))
return FALSE;
/* copy type handlers from PGconn's typeData */
if (connData->typhcnt > 0)
{
resData->typhandlers = pqt_duphandlers(
connData->typhandlers, connData->typhcnt);
if (resData->typhandlers)
resData->typhcnt = connData->typhcnt;
}
/* copy type specs from PGconn's typeData */
if (connData->typspeccnt > 0)
{
resData->typspecs = pqt_dupspecs(
connData->typspecs, connData->typspeccnt);
if (resData->typspecs)
resData->typspeccnt = connData->typspeccnt;
}
PQresultSetInstanceData((PGresult *) e->result, pqt_eventproc, resData);
break;
}
case PGEVT_RESULTCOPY:
{
PGtypeData *destData;
PGEventResultCopy *e = (PGEventResultCopy *) info;
PGtypeData *srcData = (PGtypeData *) PQresultInstanceData(
e->src, pqt_eventproc);
if (!srcData || !(destData = allocTypeData(NULL)))
return FALSE;
memcpy(&destData->fmtinfo, &srcData->fmtinfo, sizeof(PGtypeFormatInfo));
/* copy type handlers from PGresult's typeData */
if (srcData->typhcnt > 0)
{
destData->typhandlers = pqt_duphandlers(
srcData->typhandlers, srcData->typhcnt);
if (destData->typhandlers)
destData->typhcnt = srcData->typhcnt;
}
/* copy type specs from PGresult's typeData */
if (srcData->typspeccnt > 0)
{
destData->typspecs = pqt_dupspecs(
srcData->typspecs, srcData->typspeccnt);
if (destData->typspecs)
destData->typspeccnt = srcData->typspeccnt;
}
PQresultSetInstanceData(e->dest, pqt_eventproc, destData);
break;
}
case PGEVT_RESULTDESTROY:
{
PGEventResultDestroy *e = (PGEventResultDestroy *) info;
freeTypeData((PGtypeData *) PQresultInstanceData(
e->result, pqt_eventproc));
break;
}
}
return TRUE;
}
static PGtypeData *
allocTypeData(PGconn *conn)
{
PGtypeData *typeData = (PGtypeData *) malloc(sizeof(PGtypeData));
if (typeData)
{
memset(typeData, 0, sizeof(PGtypeData));
/* get type formatting info from conn */
if (conn)
pqt_getfmtinfo(conn, &typeData->fmtinfo);
}
return typeData;
}
static void
freeTypeData(PGtypeData *typeData)
{
if (typeData)
{
pqt_cleartypes(typeData);
pqt_freespecs(typeData->typspecs, typeData->typspeccnt);
typeData->typspecs = NULL;
typeData->typspeccnt = 0;
typeData->typspecmax = 0;
free(typeData);
}
}

View file

@ -0,0 +1,562 @@
/*
* exec.c
* Query execution and data retrieval functions.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
/*
* Each param value requires an oid, ptr and 2 ints (oid, value, length,
* format). This makes the maximum size of a param 20 bytes. A stack size
* of 4k would allow for 204 query params. If more params are needed,
* heap memory is used. Needing more than 204 param values in a query
* is very rare, although possible.
*/
#define PARAM_STACKSIZE 4096
#define BUILD_ARRAYS(rettype) \
rettype r; \
char *buf = NULL; \
Oid *oids = NULL; \
char **vals = NULL; \
int *lens = NULL; \
int *fmts = NULL; \
int vcnt = 0; \
char stackbuffer[PARAM_STACKSIZE]; \
\
PQseterror(NULL); \
if (!conn) \
{ \
PQseterror("PGconn cannot be NULL"); \
return (rettype)(0); \
} \
\
if (param) \
{ \
buf = stackbuffer; \
if (!buildArrays(param, &buf, &oids, &vals, &lens, &fmts)) \
return (rettype) (0); \
vcnt = param->vcnt; \
}
#define RETURN_RESULT \
if (param) \
{ \
if (buf && buf != stackbuffer) \
free(buf); \
} \
return r
static int
buildArrays(PGparam *param, char **buf, Oid **oids,
char ***vals, int **lens, int **fmts);
static PGresult *
copyExecError(PGconn *conn, PGresult *r);
static const char *
getCommand(PGconn *conn, PGparam *param, const char *command);
static int
_execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp);
int
PQgetf(const PGresult *res, int tup_num, const char *format, ...)
{
int n;
va_list ap;
va_start(ap, format);
n = PQgetvf(res, tup_num, format, ap);
va_end(ap);
return n;
}
int
PQgetvf(const PGresult *res, int tup_num, const char *format, va_list ap)
{
int r;
PGtypeHandler *h;
PGtypeArgs args;
int typpos = 0;
int flags;
Oid ftype;
PGtypeData *resData;
PGtypeSpec *spec = NULL;
char tmp[200];
PQseterror(NULL);
if (!res)
{
PQseterror("PGresult cannot be NULL");
return FALSE;
}
if (!(resData = (PGtypeData *) PQresultInstanceData(res, pqt_eventproc)))
{
PQseterror("PGresult at %p has no event data", res);
return FALSE;
}
va_copy(args.ap, ap);
/* "@name" format, lookup typeSpec in cache */
if(format && *format == '@')
{
spec = pqt_getspec(resData->typspecs, resData->typspeccnt, format + 1);
/* If we didn't find a type spec, this is an error. A format string
* with a '@' as its first character is reserved.
*/
if (!spec)
{
PQseterror("No such prepared specifier name: '%s'", format + 1);
va_end(args.ap);
return FALSE;
}
}
while (format && *format)
{
if (spec)
{
/* done, no more handlers in cached spec string. */
if (typpos == spec->idcnt)
break;
h = pqt_gethandlerbyid(resData->typhandlers, resData->typhcnt,
spec->idlist[typpos]);
/* should be an unusual, or a "will never happen", situation */
if (!h)
{
va_end(args.ap);
PQseterror("Unknown type handler id at position %d", typpos+1);
return FALSE;
}
flags = (int) spec->flags[typpos];
typpos++;
}
else
{
format = pqt_parse(format, resData->typhandlers, resData->typhcnt,
NULL, 0, &h, NULL, &typpos, &flags);
if (!format)
{
va_end(args.ap);
return FALSE;
}
if (!h)
continue;
}
if (flags & TYPFLAG_BYNAME)
args.get.field_num = PQfnumber(res, va_arg(args.ap, const char *));
else
args.get.field_num = va_arg(args.ap, int);
/* simplify life for handlers by checking getvalue's return here. */
if (args.get.field_num < 0 ||
!PQgetvalue(res, tup_num, args.get.field_num))
{
PQseterror(
"Invalid tup_num[%d].field_num[%d] (position %d)",
tup_num, args.get.field_num, typpos);
va_end(args.ap);
return FALSE;
}
ftype = PQftype(res, args.get.field_num);
if (((flags & TYPFLAG_ARRAY) && ftype != h->typoid_array) ||
(!(flags & TYPFLAG_ARRAY) && ftype != h->typoid))
{
Oid oid = (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid;
PQseterror(
"Trying to get type %u '%s' but server returned %u (position %d)",
oid, pqt_fqtn(tmp, sizeof(tmp), h->typschema, h->typname),
ftype, typpos);
va_end(args.ap);
return FALSE;
}
args.is_put = 0;
args.get.result = (PGresult *) res;
args.format = PQfformat(res, args.get.field_num);
args.fmtinfo = &resData->fmtinfo;
args.get.tup_num = tup_num;
args.is_ptr = (flags & TYPFLAG_POINTER) ? 1 : 0;
args.typpos = typpos;
args.typhandler = h;
args.errorf = pqt_argserrorf;
args.super = pqt_argssuper;
if (flags & TYPFLAG_ARRAY)
r = pqt_get_array(&args);
else
r = h->typget(&args);
if (r == -1)
{
va_end(args.ap);
return FALSE;
}
}
va_end(args.ap);
return TRUE;
}
/* --------------------------------
* Exec and Send functions
*/
PGresult *
PQexecf(PGconn *conn, const char *cmdspec, ...)
{
va_list ap;
PGresult *res;
va_start(ap, cmdspec);
res = PQexecvf(conn, cmdspec, ap);
va_end(ap);
return res;
}
PGresult *
PQexecvf(PGconn *conn, const char *cmdspec, va_list ap)
{
PGresult *res;
(void) _execvf(conn, cmdspec, ap, &res);
return res;
}
int
PQsendf(PGconn *conn, const char *cmdspec, ...)
{
int n;
va_list ap;
va_start(ap, cmdspec);
n = PQsendvf(conn, cmdspec, ap);
va_end(ap);
return n;
}
int
PQsendvf(PGconn *conn, const char *cmdspec, va_list ap)
{
return _execvf(conn, cmdspec, ap, NULL);
}
PGresult *
PQparamExec(PGconn *conn, PGparam *param, const char *command,
int resultFormat)
{
BUILD_ARRAYS(PGresult *);
command = getCommand(conn, param, command);
if (!command)
{
r = NULL;
}
else
{
r = PQexecParams(conn, command, vcnt, oids,
(const char *const * ) vals, lens, fmts, resultFormat);
pqt_setresultfields(r);
r = copyExecError(conn, r);
}
RETURN_RESULT;
}
int
PQparamSendQuery(PGconn *conn, PGparam *param, const char *command,
int resultFormat)
{
BUILD_ARRAYS(int);
command = getCommand(conn, param, command);
if (!command)
{
r = FALSE;
}
else
{
r = PQsendQueryParams(conn, command, vcnt, oids,
(const char *const * ) vals, lens, fmts, resultFormat);
if (!r)
PQseterror("PGconn: %s", PQerrorMessage(conn));
}
RETURN_RESULT;
}
PGresult *
PQparamExecPrepared(PGconn *conn, PGparam *param, const char *stmtName,
int resultFormat)
{
BUILD_ARRAYS(PGresult *);
r = PQexecPrepared(conn, stmtName, vcnt, (const char *const * ) vals,
lens, fmts, resultFormat);
pqt_setresultfields(r);
r = copyExecError(conn, r);
RETURN_RESULT;
}
int
PQparamSendQueryPrepared(PGconn *conn, PGparam *param, const char *stmtName,
int resultFormat)
{
BUILD_ARRAYS(int);
r = PQsendQueryPrepared(conn, stmtName, vcnt,
(const char *const * ) vals, lens, fmts, resultFormat);
if (!r)
PQseterror("PGconn: %s", PQerrorMessage(conn));
RETURN_RESULT;
}
/* Called by PQexecvf and PQsendvf. When resp is NULL, PQparamSendQuery
* is used to execute the command. Otherwise, PQparamExec is used. The
* return value is always zero when resp is not NULL. When resp is NULL,
* the return value is zero for error and non-zero for success (identical
* to PQparamSendQuery).
*/
static int
_execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp)
{
int retval = 0;
size_t stmt_len=0;
char buffer[8192]; /* could be larger these days but be conservative */
char *stmt = NULL;
PGparam *param = NULL;
if (resp)
*resp = NULL;
if(!conn)
{
PQseterror("PGconn cannot be NULL");
return FALSE;
}
if(!cmdspec || !*cmdspec)
{
PQseterror("cmdspec cannot be NULL or an empty string");
return FALSE;
}
/* No stmt buf required for preapred type specs */
if (*cmdspec != '@')
{
/* The resulting parameterized command is guarenteed to be smaller
* than the cmdspec. When needed, enlarge stmt buf to cmdspec length.
*/
stmt_len = strlen(cmdspec) + 1;
/* stack buffer is too small, use heap */
if (stmt_len > sizeof(buffer))
{
if (!(stmt = (char *) malloc(stmt_len)))
{
PQseterror(PQT_OUTOFMEMORY);
return FALSE;
}
}
else
{
stmt = buffer;
stmt_len = sizeof(buffer);
}
}
if ((param = PQparamCreate(conn)))
{
if (PQputvf(param, stmt, stmt_len, cmdspec, ap))
{
const char *s = stmt ? stmt : cmdspec;
if (resp)
*resp = PQparamExec(conn, param, s, 1);
else
retval = PQparamSendQuery(conn, param, s, 1);
}
PQparamClear(param);
}
if (stmt && stmt != buffer)
free(stmt);
return retval;
}
/*
* Assigns param values to param arrays, for use with postgres
* parameter API. 'buf' is expected to be PARAM_STACKSIZE bytes. If more
* memory is required, memory is allocated and assigned to *buf, which
* must be freed by caller. To determine if *buf was allocated, compare
* its address to the initially provided stack address.
* Returns 1 on success and 0 on error.
*/
static int
buildArrays(PGparam *param, char **buf, Oid **oids,
char ***vals, int **lens, int **fmts)
{
int n;
/* no params to assign */
if (param->vcnt == 0)
return 1;
/* required memory size for the 4 param arrays */
n = (int) ((sizeof(void *) * param->vcnt) + /* values */
((sizeof(int) * 2) * param->vcnt) + /* lengths and formats */
(sizeof(Oid) * param->vcnt)); /* oids */
/* required memory is too large for stack buffer, get some heap */
if (n > PARAM_STACKSIZE)
{
char *p;
if (!(p = (char *) malloc(n)))
{
PQseterror(PQT_OUTOFMEMORY);
return 0;
}
*buf = p;
}
/* give arrays memory from buffer, which could be stack or heap. */
*vals = (char **) *buf;
*lens = (int *) (*buf + (sizeof(void *) * param->vcnt));
*fmts = (*lens) + param->vcnt;
*oids = (Oid *) ((*fmts) + param->vcnt);
/* loop param values and assign value, length, format
* and oid to arrays.
*/
for (n=0; n < param->vcnt; n++)
{
(*oids)[n] = param->vals[n].oid;
(*vals)[n] = param->vals[n].data;
(*lens)[n] = param->vals[n].datal;
(*fmts)[n] = param->vals[n].format;
}
return 1;
}
static PGresult *
copyExecError(PGconn *conn, PGresult *r)
{
if (!r)
{
PQseterror("PGconn: %s", PQerrorMessage(conn));
return NULL;
}
switch (PQresultStatus(r))
{
case PGRES_COMMAND_OK:
case PGRES_TUPLES_OK:
case PGRES_EMPTY_QUERY:
break;
default:
{
PQseterror("PGresult: %s", PQresultErrorMessage(r));
PQclear(r);
r = NULL;
break;
}
}
return r;
}
/* Using the param is preferred when both conn and param are provided.
* The conn is there in case the exec has no parameters, NULL param.
*/
static const char *
getCommand(PGconn *conn, PGparam *param, const char *command)
{
PGtypeSpec *spec;
int typspeccnt = 0;
PGtypeSpec *typspecs = NULL;
if (!command)
{
PQseterror("command to execute cannot be NULL");
return NULL;
}
if (*command != '@')
return command;
if (param)
{
typspecs = param->typspecs;
typspeccnt = param->typspeccnt;
}
/* Try to get instance data from the conn */
if (!typspecs || typspeccnt == 0)
{
PGtypeData *data = PQinstanceData(conn, pqt_eventproc);
if (!data)
{
PQseterror("PGconn at %p has no event data", conn);
return NULL;
}
typspecs = data->typspecs;
typspeccnt = data->typspeccnt;
}
spec = pqt_getspec(typspecs, typspeccnt, command + 1);
/* If we didn't find a type spec, this is an error. A format string
* with an '@' as its first character is reserved.
*/
if (!spec)
{
PQseterror("No such prepared specifier name: '%s'", command + 1);
return NULL;
}
/* make sure type spec was prepared with a statement */
if (!spec->stmt || !*spec->stmt)
{
PQseterror("Prepared specifier name '%s' has no statement", command + 1);
return NULL;
}
return (const char *) spec->stmt;
}

View file

@ -0,0 +1,391 @@
/*
* geo.c
* Type handler for the geometric data types.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
static int text2point(PGpoint *pt, char *text, char **endptr);
static int text2points(PGtypeArgs *args, PGpoint **pts, int *npts);
static int bin2points(PGtypeArgs *args, char *valp, int ptcnt,
PGpoint **pts, int *npts);
static int putpoints(PGtypeArgs *args, int npts, PGpoint *pts,
int is_path, int closed);
int
pqt_put_point(PGtypeArgs *args)
{
unsigned int *buf;
PGpoint *pt = va_arg(args->ap, PGpoint *);
PUTNULLCHK(args, pt);
buf = (unsigned int *) args->put.out;
pqt_swap8(buf, &pt->x, 1);
pqt_swap8(buf + 2, &pt->y, 1);
return 16;
}
int
pqt_get_point(PGtypeArgs *args)
{
DECLVALUE(args);
PGpoint *pt = va_arg(args->ap, PGpoint *);
CHKGETVALS(args, pt);
if (args->format == TEXTFMT)
{
if (!text2point(pt, value, NULL))
RERR_STR2INT(args);
return 0;
}
pqt_swap8(&pt->x, (unsigned int *) value, 0);
pqt_swap8(&pt->y, ((unsigned int *) (value)) + 2, 0);
return 0;
}
int
pqt_put_lseg(PGtypeArgs *args)
{
unsigned int *buf;
PGlseg *lseg = va_arg(args->ap, PGlseg *);
PUTNULLCHK(args, lseg);
buf = (unsigned int *) args->put.out;
pqt_swap8(buf, &lseg->pts[0].x, 1);
pqt_swap8(buf + 2, &lseg->pts[0].y, 1);
pqt_swap8(buf + 4, &lseg->pts[1].x, 1);
pqt_swap8(buf + 6, &lseg->pts[1].y, 1);
return 32;
}
int
pqt_get_lseg(PGtypeArgs *args)
{
DECLVALUE(args);
unsigned int *v;
PGlseg *lseg = va_arg(args->ap, PGlseg *);
CHKGETVALS(args, lseg);
if (args->format == TEXTFMT)
{
PGpoint *pts = (PGpoint *)lseg;
if (*value++ != '[' ||
!text2point(pts, value, &value) ||
*value++ != ',' ||
!text2point(pts + 1, value, &value) ||
*value != ']')
RERR_STR2INT(args);
return 0;
}
v = (unsigned int *) value;
pqt_swap8(&lseg->pts[0].x, v, 0);
pqt_swap8(&lseg->pts[0].y, v + 2, 0);
pqt_swap8(&lseg->pts[1].x, v + 4, 0);
pqt_swap8(&lseg->pts[1].y, v + 6, 0);
return 0;
}
int
pqt_put_box(PGtypeArgs *args)
{
unsigned int *buf;
PGbox *box = va_arg(args->ap, PGbox *);
PUTNULLCHK(args, box);
buf = (unsigned int *) args->put.out;
pqt_swap8(buf, &box->high.x, 1);
pqt_swap8(buf + 2, &box->high.y, 1);
pqt_swap8(buf + 4, &box->low.x, 1);
pqt_swap8(buf + 6, &box->low.y, 1);
return 32;
}
int
pqt_get_box(PGtypeArgs *args)
{
DECLVALUE(args);
unsigned int *v;
PGbox *box = va_arg(args->ap, PGbox *);
CHKGETVALS(args, box);
if (args->format == TEXTFMT)
{
PGpoint *pts = (PGpoint *)box;
if (!text2point(pts, value, &value) ||
*value++ != ',' ||
!text2point(pts + 1, value, NULL))
RERR_STR2INT(args);
return 0;
}
v = (unsigned int *) value;
pqt_swap8(&box->high.x, v, 0);
pqt_swap8(&box->high.y, v + 2, 0);
pqt_swap8(&box->low.x, v + 4, 0);
pqt_swap8(&box->low.y, v + 6, 0);
return 0;
}
int
pqt_put_circle(PGtypeArgs *args)
{
unsigned int *buf;
PGcircle *circle = va_arg(args->ap, PGcircle *);
PUTNULLCHK(args, circle);
buf = (unsigned int *) args->put.out;
pqt_swap8(buf, &circle->center.x, 1);
pqt_swap8(buf + 2, &circle->center.y, 1);
pqt_swap8(buf + 4, &circle->radius, 1);
return 24;
}
int
pqt_get_circle(PGtypeArgs *args)
{
DECLVALUE(args);
unsigned int *v;
PGcircle *circle = va_arg(args->ap, PGcircle *);
CHKGETVALS(args, circle);
if (args->format == TEXTFMT)
{
if (*value++ != '<' ||
!text2point((PGpoint *)circle, value, &value) ||
*value++ != ',' ||
!pqt_text_to_float8(&circle->radius, value, &value) ||
*value != '>')
RERR_STR2INT(args);
return 0;
}
v = (unsigned int *) value;
pqt_swap8(&circle->center.x, v, 0);
pqt_swap8(&circle->center.y, v + 2, 0);
pqt_swap8(&circle->radius, v + 4, 0);
return 0;
}
int
pqt_put_path(PGtypeArgs *args)
{
PGpath *path = va_arg(args->ap, PGpath *);
PUTNULLCHK(args, path);
return putpoints(args, path->npts, path->pts, 1, path->closed ? 1 : 0);
}
int
pqt_get_path(PGtypeArgs *args)
{
DECLVALUE(args);
PGpath *path = va_arg(args->ap, PGpath *);
CHKGETVALS(args, path);
if (args->format == TEXTFMT)
{
path->closed = *value == '(' ? 1 : 0;
return text2points(args, &path->pts, &path->npts);
}
path->closed = *value ? 1 : 0;
value++;
return bin2points(args,
value + sizeof(int), /* beginning of point array */
pqt_buf_getint4(value), /* number of points */
&path->pts, &path->npts);
}
int
pqt_put_polygon(PGtypeArgs *args)
{
PGpolygon *polygon = va_arg(args->ap, PGpolygon *);
PUTNULLCHK(args, polygon);
return putpoints(args, polygon->npts, polygon->pts, 0, 0);
}
int
pqt_get_polygon(PGtypeArgs *args)
{
DECLVALUE(args);
PGpolygon *polygon = va_arg(args->ap, PGpolygon *);
CHKGETVALS(args, polygon);
if (args->format == TEXTFMT)
return text2points(args, &polygon->pts, &polygon->npts);
return bin2points(args,
value + sizeof(int), /* beginning of point array */
pqt_buf_getint4(value), /* number of points */
&polygon->pts,
&polygon->npts);
}
static int
putpoints(PGtypeArgs *args, int npts, PGpoint *pts,
int is_path, int closed)
{
int i;
int datal;
int hdr = (int) sizeof(int);
char *out;
/* pts is for a path, include 1 byte open/closed flag */
if (is_path)
hdr++;
/* length of binary formated path */
datal = (npts * sizeof(PGpoint)) + hdr;
/* make sure out is large enough */
if (args->put.expandBuffer(args, datal) == -1)
return -1;
out = args->put.out;
if (is_path)
*out++ = closed ? 1 : 0; /* path open/closed flag */
/* write the number of points as an int32 */
pqt_buf_putint4(out, npts);
out += 4;
/* assign points to the data 'out' buffer */
for (i=0; i < npts; i++)
{
pqt_swap8(out, &pts[i].x, 1);
out += sizeof(double);
pqt_swap8(out, &pts[i].y, 1);
out += sizeof(double);
}
return datal;
}
static int
text2points(PGtypeArgs *args, PGpoint **pts, int *npts)
{
DECLVALUE(args);
char *s;
int cnt = 0;
PGpoint *p = NULL;
*pts = NULL;
*npts = 0;
if (*value != '(' && *value != '[')
RERR(args, "malformed point array");
/* get the number of points by counting the '(' */
for (s=value+1; *s; s++)
{
if (*s == '(')
{
if (!(s = strchr(s, ')'))) /* skip point contents */
break;
cnt++;
}
}
if (cnt == 0)
return 0; /* empty point list */
p = (PGpoint *) PQresultAlloc((PGresult *) args->get.result,
cnt * sizeof(PGpoint));
if (!p)
RERR_MEM(args);
for (cnt=0; *++value; )
{
if (!text2point(&p[cnt++], value, &value))
RERR_STR2INT(args);
/* done */
if (*value != ',')
break;
}
*pts = p;
*npts = cnt;
return 0;
}
static int
bin2points(PGtypeArgs *args, char *valp, int ptcnt,
PGpoint **pts, int *npts)
{
int i;
PGpoint *p;
*pts = NULL;
*npts = 0;
if (ptcnt == 0)
return 0;
p = (PGpoint *) PQresultAlloc((PGresult *) args->get.result,
ptcnt * sizeof(PGpoint));
if (!p)
RERR_MEM(args);
for (i=0; i < ptcnt; i++)
{
pqt_swap8(&p[i].x, valp, 0);
valp += sizeof(double);
pqt_swap8(&p[i].y, valp, 0);
valp += sizeof(double);
}
*pts = p;
*npts = ptcnt;
return 0;
}
static int
text2point(PGpoint *pt, char *text, char **endptr)
{
if (*text++ != '(')
return 0;
if (!pqt_text_to_float8(&pt->x, text, &text) || *text++ != ',')
return 0;
if (!pqt_text_to_float8(&pt->y, text, &text) || *text++ != ')')
return 0;
if (endptr)
*endptr = text;
return 1;
}

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2003-2008, PostgreSQL Global Development Group
* $PostgreSQL: pgsql/src/include/getaddrinfo.h
*/
#if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \
!defined(HAVE_GETADDRINFO))
#ifndef GETADDRINFO_H
#define GETADDRINFO_H
#include <sys/socket.h>
#define _XOPEN_SOURCE_EXTENDED 1
#include <netdb.h>
/* Various macros that ought to be in <netdb.h>, but might not be */
#ifndef EAI_FAIL
#define EAI_BADFLAGS (-1)
#define EAI_NONAME (-2)
#define EAI_AGAIN (-3)
#define EAI_FAIL (-4)
#define EAI_FAMILY (-6)
#define EAI_SOCKTYPE (-7)
#define EAI_SERVICE (-8)
#define EAI_MEMORY (-10)
#define EAI_SYSTEM (-11)
#endif /* !EAI_FAIL */
#ifndef AI_PASSIVE
#define AI_PASSIVE 0x0001
#endif
#ifndef AI_NUMERICHOST
/*
* some platforms don't support AI_NUMERICHOST; define as zero if using
* the system version of getaddrinfo...
*/
#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
#define AI_NUMERICHOST 0
#else
#define AI_NUMERICHOST 0x0004
#endif
#endif
#ifndef NI_NUMERICHOST
#define NI_NUMERICHOST 1
#endif
#ifndef NI_NUMERICSERV
#define NI_NUMERICSERV 2
#endif
#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif
#ifndef NI_MAXSERV
#define NI_MAXSERV 32
#endif
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
#ifndef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY
#define ss_family __ss_family
#else
#error struct sockaddr_storage does not provide an ss_family member
#endif
#endif
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN
#define ss_len __ss_len
#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1
#endif
#else /* !HAVE_STRUCT_SOCKADDR_STORAGE */
/* Define a struct sockaddr_storage if we don't have one. */
#ifndef __CYGWIN__
struct sockaddr_storage
{
union
{
struct sockaddr sa; /* get the system-dependent fields */
long long int ss_align; /* ensures struct is properly aligned */
char ss_pad[128]; /* ensures struct has desired size */
} ss_stuff;
};
#endif
#define ss_family ss_stuff.sa.sa_family
/* It should have an ss_len field if sockaddr has sa_len. */
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
#define ss_len ss_stuff.sa.sa_len
#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1
#endif
#endif /* HAVE_STRUCT_SOCKADDR_STORAGE */
#ifndef HAVE_STRUCT_ADDRINFO
struct addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
#endif /* HAVE_STRUCT_ADDRINFO */
/* Rename private copies per comments above */
#ifdef getaddrinfo
#undef getaddrinfo
#endif
#ifdef freeaddrinfo
#undef freeaddrinfo
#endif
#ifdef gai_strerror
#undef gai_strerror
#endif
#ifdef getnameinfo
#undef getnameinfo
#endif
extern int getaddrinfo(const char *node, const char *service,
const struct addrinfo * hints, struct addrinfo ** res);
extern void freeaddrinfo(struct addrinfo * res);
extern const char *gai_strerror(int errcode);
extern int getnameinfo(const struct sockaddr * sa, int salen,
char *node, int nodelen,
char *service, int servicelen, int flags);
#endif /* GETADDRINFO_H */
#endif /* HAVE_GETADDRINFO */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,533 @@
/*
* libpqtypes-int.h
* Private header file for libpqtypes. All source files include
* this header.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#ifndef LIBPQTYPES_INT_H
#define LIBPQTYPES_INT_H
#define PLN do{ \
printf("%s:%d\n", __FUNCTION__, __LINE__); \
fflush(stdout); \
}while(0)
#ifdef HAVE_CONFIG_H
# include "pqt_config.h"
#endif
/* Using the windows compiler */
#ifdef _MSC_VER
# define PQT_MSVC _MSC_VER
#endif
/* We can/should use the Windows API */
#if defined(PQT_MSVC) || defined(__MINGW32__)
# define PQT_WINAPI
#endif
/* Compiling on a windows platform */
#if defined(PQT_WINAPI) || defined(__CYGWIN__)
# define PQT_WIN32
#endif
/* WINAPI is available, include needed winsock headers */
#ifdef PQT_WINAPI
# ifdef PQT_MSVC
# pragma warning (disable : 4706 4100 4711 4127 4702)
# pragma warning (disable: 4706 4100 4514 4710 4201 4206)
# endif
# include <winsock2.h>
# include <ws2tcpip.h>
# include <Wspiapi.h> /* need for getaddrinfo (2000 and below */
# include <windows.h>
#else
# if defined(__CYGWIN__) || defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
# endif
# if defined(__CYGWIN__) || defined(HAVE_SYS_SOCKET_H)
# include <sys/socket.h>
# endif
# if defined(__CYGWIN__) || defined(HAVE_NETDB_H)
# include <netdb.h>
# endif
# if defined(__CYGWIN__) || defined(HAVE_NETINET_IN_H)
# include <netinet/in.h>
# endif
# if defined(__CYGWIN__) || defined(HAVE_ARPA_INET_H)
# include <arpa/inet.h>
# endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
/* string*.h, windows platforms just include string.h */
#ifdef PQT_WIN32
# include <string.h>
#else
# ifdef HAVE_STRING_H
# include <string.h>
# endif
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif
/* Include stddef.h if on windows or if we have it */
#if defined(PQT_WIN32) || defined(HAVE_STDDEF_H)
# include <stddef.h>
#endif
/* Include limits.h if on windows or if we have it */
#if defined(PQT_WIN32) || defined(HAVE_LIMITS_H)
# include <limits.h>
#endif
/* Include linux/limits.h if available */
#if defined(HAVE_CONFIG_H) && defined(HAVE_LINUX_LIMITS_H)
# include <linux/limits.h>
#endif
/* Include math.h if on windows or if we have it */
#if defined(PQT_WIN32) || defined(HAVE_MATH_H)
# include <math.h>
#endif
/* Include the public API, pulls in libpq-fe.h for us. */
#include "libpqtypes.h"
#include "libpq-events.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
#ifndef NULL_LEN
# define NULL_LEN (-1)
#endif
/* Win32 builds use _timezone and _tzname: manually define HAVE_TZNAME. */
#ifdef PQT_WIN32
# define HAVE_TZNAME
# define pqt_timezone _timezone
# define pqt_tzname _tzname
#else
# define pqt_timezone timezone
# define pqt_tzname tzname
#endif
#define PQT_OUTOFMEMORY "Out of memory error"
#define PQT_MAXIDLEN 64
#define TEXTFMT 0
#define BINARYFMT 1
#define TYPFLAG_CASEFOLD 0x01
#define TYPFLAG_ARRAY 0x02
#define TYPFLAG_POINTER 0x04
#define TYPFLAG_INVALID 0x08
#define TYPFLAG_BYNAME 0x10
/* Avoid passing NULL to realloc, some systems don't support it */
#define pqt_realloc(ptr, size) (ptr) ? realloc(ptr, size) : malloc(size)
#define countof(array) (int) (sizeof(array) / sizeof(array[0]))
/* MSVC 8 deprecated quite a few POSIX names */
#if defined(PQT_MSVC) && PQT_MSVC >= 1400
# undef strdup
# undef sscanf
# define strdup _strdup
# define sscanf sscanf_s
#endif
/* define va_copy */
#ifndef va_copy
# ifdef __va_copy
# define va_copy(dest, src) __va_copy(dest, src)
# else
# define va_copy(dest, src) (dest) = (src)
# endif
#endif
#define pqt_hex_to_dec(v) \
(unsigned char) (((v) > '9') ? ((v) - 'a') + 10 : (v) - '0')
/* --------------------------------
* Macros used by built-in handlers
*/
/* used by get handlers */
#define DECLLENGTH(args) \
int valuel = PQgetlength((args)->get.result, (args)->get.tup_num, \
(args)->get.field_num)
/* used by get handlers */
#define DECLVALUE(getargs) \
char *value = PQgetvalue((getargs)->get.result, (getargs)->get.tup_num, \
(getargs)->get.field_num)
/* used by put handlers */
#define PUTNULLCHK(args, valp) do{ \
if (!(valp)) \
return pqt_put_null(args); \
}while (0)
/* used by get handlers, checks for NULL pointers and PQgetisnull */
#define CHKGETVALS(args, outp) do{ \
if (!(outp)) \
RERR(args, "output pointer is NULL"); \
memset(outp, 0, sizeof(*(outp))); \
if (PQgetisnull((args)->get.result, (args)->get.tup_num, \
(args)->get.field_num)) \
return 0; \
}while (0)
/* RERR for return error: errorf always returns -1 */
#define RERR(_args, msg) return (_args)->errorf(_args, msg)
#define RERR_STR2INT(args) RERR(args, "String to integer conversion failed")
#define RERR_MEM(args) RERR(args, PQT_OUTOFMEMORY)
#ifdef STRICT_MEMORY_ALIGNMENT
# define pqt_buf_putint2(_buffer, _val) do{ \
short _v = (short) htons((short) (_val)); \
memcpy(_buffer, &_v, sizeof(short)); \
} while (0)
# define pqt_buf_putint4(_buffer, _val) do{ \
int _v = (int) htonl((int) (_val)); \
memcpy(_buffer, &_v, sizeof(int)); \
} while (0)
short pqt_buf_getint2(char *buffer);
int pqt_buf_getint4(char *buffer);
#else
# define pqt_buf_putint2(_out, _val) \
*(short *) (_out) = (short) htons((short) (_val))
# define pqt_buf_getint2(_buffer) (short) ntohs(*(short *) (_buffer))
# define pqt_buf_putint4(_out, _val) \
*(int *) (_out) = (int) htonl((int) (_val))
# define pqt_buf_getint4(_buffer) (int) ntohl(*(int *) (_buffer))
#endif
/* ----------------------------------
* See: src/include/catalog/pg_type.h
* Used by built-in type handlers
*/
/* numerics types */
#define INT2OID 21
#define INT4OID 23
#define INT8OID 20
#define FLOAT4OID 700
#define FLOAT8OID 701
#define NUMERICOID 1700
/* geo types */
#define POINTOID 600
#define LSEGOID 601
#define PATHOID 602
#define BOXOID 603
#define POLYGONOID 604
#define LINEOID 628 /* not supported yet */
#define CIRCLEOID 718
/* network types */
#define INETOID 869
#define CIDROID 650
#define MACADDROID 829
/* variable length types */
#define BPCHAROID 1042
#define VARCHAROID 1043
#define NAMEOID 19
#define TEXTOID 25
#define ZPBITOID 1560 /* not supported yet */
#define VARBITOID 1562 /* not supported yet */
#define BYTEAOID 17
/* date and time types */
#define DATEOID 1082
#define TIMEOID 1083
#define TIMETZOID 1266
#define TIMESTAMPOID 1114
#define TIMESTAMPTZOID 1184
#define INTERVALOID 1186
/* misc types */
#define CHAROID 18
#define BOOLOID 16
#define OIDOID 26
#define CASHOID 790
#define RECORDOID 2249
#define UUIDOID 2950
/* --------------------------------
* Private Structures
*/
/* Represents a param value. An array of PGvalues was choosen over
* allocating 4 separate arrays: oids, values, lengths, formats. Instead
* of 4 allocations you only have one. The PGvalue array can easily be
* converted to 4 arrays for execution.
*/
typedef struct
{
int ptrl; /* Length of value's pointer */
void *ptr; /* value pointer, data member uses this for non-NULL values */
int datal; /* Length of current value: always <= ptrl */
char *data; /* current value data, can be NULL ... thus the ptr member. */
int format; /* format: 0=text, 1=binary */
Oid oid; /* Oid of the data */
} PGvalue;
/* performance driven structure. Instead of parsing and looking up
* type specs each put or get, PQspecPrepare can be used to compile
* a spec format string into a PGtypeSpec object. The biggest wins are
* large results sets and arrays.
*/
typedef struct
{
char *name; /* name for the prepared spec, used for lookup. */
char *stmt; /* parameterized version of stmt, if supplied */
int idcnt; /* number of handler ids */
int *idlist; /* handler ids */
unsigned char *flags; /* handler flags */
} PGtypeSpec;
/* PGparam structure */
struct pg_param
{
int vcnt; /* current number of param values */
int vmax; /* number of PGvalue structs in 'vals' array. */
PGvalue *vals; /* array of param values, grown when needed */
/* assigned during PQparamCreate */
PGtypeFormatInfo fmtinfo;
int typhcnt;
PGtypeHandler *typhandlers;
int typspeccnt;
PGtypeSpec *typspecs;
};
typedef struct
{
PGtypeFormatInfo fmtinfo;
int typhcnt;
int typhmax;
PGtypeHandler *typhandlers;
int typspeccnt;
int typspecmax;
PGtypeSpec *typspecs;
} PGtypeData;
/* --------------------------------
* Internal API functions (not exported)
*/
/* === in events.c === */
/* The libpq PGEventProc */
int pqt_eventproc(PGEventId id, void *info, void *passThrough);
void pqt_cleartypes(PGtypeData *typeData);
/* === in param.c === */
int pqt_putparam(PGparam *param, const void *data, int datal,
int flags, int format, Oid typoid);
/* === in spec.c === */
PGtypeSpec *pqt_getspec(PGtypeSpec *specs, int count, const char *name);
PGtypeSpec *pqt_dupspecs(PGtypeSpec *specs, int count);
void pqt_clearspec(PGtypeSpec *cache);
void pqt_freespecs(PGtypeSpec *specs, int count);
char *pqt_parse(const char *format, PGtypeHandler *h, int hcnt,
char *stmtBuf, size_t stmtBufLen, PGtypeHandler **out, size_t *stmtPos,
int *typpos, int *flags);
char *pqt_parsetype(const char *spec, char *schema, char *typname,
int *flags, int typpos);
/* === in handler.c === */
int pqt_allowsptr(PGtypeHandler *h);
void pqt_getfmtinfo(const PGconn *conn, PGtypeFormatInfo *info);
PGtypeHandler *pqt_duphandlers(PGtypeHandler *handlers, int hcnt);
void pqt_freehandlers(PGtypeHandler *handlers, int hcnt);
int pqt_argssuper(PGtypeArgs *args, ...);
int pqt_argserrorf(PGtypeArgs *args, const char *format, ...);
void pqt_setresultfields(const PGresult *res);
PGtypeHandler *pqt_gethandler(PGtypeHandler *handlers, int hcnt,
const char *schema, const char *typname);
PGtypeHandler *pqt_gethandlerbyid(PGtypeHandler *handlers,
int hcnt, int id);
/* FQTN standards for Fully Qualified Type Name. Returns a pointer to out.
* Only returns NULL if out is NULL or outl <= 0.
*/
char *pqt_fqtn(char *out, size_t outl,
const char *schema, const char *typname);
/* == in utils.c == */
PGresult *pqt_copyresult(PGtypeArgs *args, int nattrs);
void pqt_swap8(void *outp, void *inp, int tonet);
int pqt_text_to_int8(char *val, void *out);
int pqt_text_to_float8(double *f8, char *text, char **endptr);
/* Checks buffer and truncates 'src' if 'dest' is too small. */
char *pqt_strcpy(char *dest, size_t size, const char *src);
/* Taken from postgres project (named pg_xxx) */
unsigned char pqt_tolower(unsigned char ch);
int pqt_strcasecmp(const char *s1, const char *s2);
int pqt_strncasecmp(const char *s1, const char *s2, size_t n);
/* == in port.c == */
/* Define a strtoll macro for windows, except for MinGW & Cygwin
* which always have it. MSVC has _strtoi64, but MSVC 6 and under
* require that we declare the prototype.
*/
#ifdef PQT_MSVC
# if PQT_MSVC <= 1200
__int64 __cdecl _strtoi64(const char *, char **, int);
# endif
# define strtoll _strtoi64
#endif
/* In rare cases these could be absent (older platforms). We implement
* drop-in replacements in port.c for those cases. strtod handled by
* configure, which adds a replacement for us.
*
* NOTE: only unixes use configure so sections wrapped around
* HAVE_CONFIG_H exclude windows.
*/
#ifdef HAVE_CONFIG_H
# ifndef HAVE_STRTOL
long strtol(const char *nptr, char **endptr, int base);
# endif
# ifndef HAVE_STRTOUL
unsigned long strtoul(const char *nptr, char **endptr, int base);
# endif
# ifndef HAVE_STRTOLL
long long int strtoll(const char *nptr, char **endptr, int base);
# endif
#endif
/* Handles platform differences and always returns -1 for failure.
* On windows: MSVC 8 uses _snprintf_s, otherwise _snprintf.
*/
int pqt_snprintf(char *buf, size_t size, const char *format, ...);
int pqt_vsnprintf(char *buf, size_t size, const char *format, va_list ap);
/* --------------------------------
* Built-in Type Handlers
*/
/* === in array.c === */
int pqt_put_array(PGtypeArgs *args);
int pqt_get_array(PGtypeArgs *args);
/* == in datetime.c == */
int pqt_put_date(PGtypeArgs *args);
int pqt_get_date(PGtypeArgs *args);
int pqt_put_time(PGtypeArgs *args);
int pqt_get_time(PGtypeArgs *args);
int pqt_put_timetz(PGtypeArgs *args);
int pqt_get_timetz(PGtypeArgs *args);
int pqt_put_timestamp(PGtypeArgs *args);
int pqt_get_timestamp(PGtypeArgs *args);
int pqt_put_timestamptz(PGtypeArgs *args);
int pqt_get_timestamptz(PGtypeArgs *args);
int pqt_put_interval(PGtypeArgs *args);
int pqt_get_interval(PGtypeArgs *args);
/* == in geo.c == */
int pqt_put_point(PGtypeArgs *args);
int pqt_get_point(PGtypeArgs *args);
int pqt_put_lseg(PGtypeArgs *args);
int pqt_get_lseg(PGtypeArgs *args);
int pqt_put_box(PGtypeArgs *args);
int pqt_get_box(PGtypeArgs *args);
int pqt_put_circle(PGtypeArgs *args);
int pqt_get_circle(PGtypeArgs *args);
int pqt_put_path(PGtypeArgs *args);
int pqt_get_path(PGtypeArgs *args);
int pqt_put_polygon(PGtypeArgs *args);
int pqt_get_polygon(PGtypeArgs *args);
/* == in misc.c == */
int pqt_put_char(PGtypeArgs *args); /* "char" type, not char(N) */
int pqt_get_char(PGtypeArgs *args);
int pqt_put_bool(PGtypeArgs *args);
int pqt_get_bool(PGtypeArgs *args);
int pqt_put_money(PGtypeArgs *args);
int pqt_get_money(PGtypeArgs *args);
int pqt_put_uuid(PGtypeArgs *args);
int pqt_get_uuid(PGtypeArgs *args);
int pqt_put_null(PGtypeArgs *args); /* no get for null */
/* == in network.c == */
int pqt_put_inet(PGtypeArgs *args); /* handles cidr */
int pqt_get_inet(PGtypeArgs *args);
int pqt_get_cidr(PGtypeArgs *args);
int pqt_put_macaddr(PGtypeArgs *args);
int pqt_get_macaddr(PGtypeArgs *args);
/* == in numerics.c == */
int pqt_put_int2(PGtypeArgs *args);
int pqt_get_int2(PGtypeArgs *args);
int pqt_put_int4(PGtypeArgs *args); /* handles oid */
int pqt_get_int4(PGtypeArgs *args); /* handles oid */
int pqt_put_int8(PGtypeArgs *args);
int pqt_get_int8(PGtypeArgs *args);
int pqt_put_float4(PGtypeArgs *args);
int pqt_get_float4(PGtypeArgs *args);
int pqt_put_float8(PGtypeArgs *args);
int pqt_get_float8(PGtypeArgs *args);
int pqt_put_numeric(PGtypeArgs *args);
int pqt_get_numeric(PGtypeArgs *args);
/* == record.c (composites) == */
int pqt_put_record(PGtypeArgs *args);
int pqt_get_record(PGtypeArgs *args);
/* == in varlena.c == */
int pqt_put_str(PGtypeArgs *args); /* no get for str - use PQgetvalue */
int pqt_put_text(PGtypeArgs *args); /* handles varchar, bpchar and name */
int pqt_get_text(PGtypeArgs *args); /* handles varchar, bpchar and name */
int pqt_put_bytea(PGtypeArgs *args);
int pqt_get_bytea(PGtypeArgs *args);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,468 @@
/*
* libpqtypes.h
* Public header for libpqtypes. Contains the entire public API.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#ifndef LIBPQTYPES_H
#define LIBPQTYPES_H
#include <libpq-fe.h>
#include <time.h>
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__)
# define PQT_EXPORT __declspec(dllexport)
#else
# define PQT_EXPORT extern
#endif
/* MSVC 6 must use `i64', everything else uses `LL'. */
#if !defined(_MSC_VER) || _MSC_VER > 1200
# define PQT_INT64CONST(x) ((PGint8) x##LL)
#else
# define PQT_INT64CONST(x) ((PGint8) x##i64)
#endif
enum
{
PQT_SUBCLASS,
PQT_COMPOSITE,
PQT_USERDEFINED
};
typedef struct pg_param PGparam;
typedef struct pg_typeargs PGtypeArgs;
typedef int (*PGtypeProc)(PGtypeArgs *args);
/* For use with a PQregisterXXX function */
typedef struct
{
const char *typname;
PGtypeProc typput;
PGtypeProc typget;
} PGregisterType;
typedef struct
{
int sversion;
int pversion;
char datestyle[48];
int integer_datetimes;
} PGtypeFormatInfo;
/* Record Attribute Description, its columns */
typedef struct
{
Oid attoid;
int attlen;
int atttypmod;
char attname[65];
} PGrecordAttDesc;
/* Type handler for putf and getf functions. The char fixed length buffers
* used to be allocated pointers. This was a performance problem when
* many type handlers are registered and one uses getf on a composite or
* an array. These types require generating a PGresult and duplicating
* the type handlers. Saved 40% by not having to deep copy the strings.
*/
typedef struct pg_typhandler
{
int id;
char typschema[65];
char typname[65];
int typlen;
Oid typoid;
Oid typoid_array;
PGtypeProc typput;
PGtypeProc typget;
int base_id;
/* For composites, contains each attribute of a composite */
int nattrs;
int freeAttDescs;
PGrecordAttDesc attDescsBuf[16];
PGrecordAttDesc *attDescs;
} PGtypeHandler;
/* Values required during a type handler put ot get operation. */
struct pg_typeargs
{
int is_put;
const PGtypeFormatInfo *fmtinfo;
int is_ptr;
int format;
va_list ap;
int typpos;
PGtypeHandler *typhandler;
int (*errorf)(PGtypeArgs *args, const char *format, ...);
int (*super)(PGtypeArgs *args, ...);
struct
{
PGparam *param;
char *out;
char *__allocated_out; /* leave me alone! */
int outl;
int (*expandBuffer)(PGtypeArgs *args, int new_len);
} put;
struct
{
PGresult *result;
int tup_num;
int field_num;
} get;
};
/* ----------------
* Variable Length types
* ----------------
*/
typedef char *PGtext;
typedef char *PGvarchar;
typedef char *PGbpchar;
typedef char *PGuuid;
typedef struct
{
int len;
char *data;
} PGbytea;
/* ----------------
* Numeric types
* ----------------
*/
typedef signed char PGchar;
typedef int PGbool;
typedef short PGint2;
typedef int PGint4;
typedef float PGfloat4;
typedef double PGfloat8;
typedef char *PGnumeric;
/* Defined by an end-user if the system is missing long long. */
#ifdef PQT_LONG_LONG
typedef PQT_LONG_LONG PGint8;
typedef PQT_LONG_LONG PGmoney;
/* MinGW and MSVC can use __int64 */
#elif defined(__MINGW32__) || defined(_MSC_VER)
typedef __int64 PGint8;
typedef __int64 PGmoney;
/* Cygwin and Unixes. */
#else
typedef long long PGint8;
typedef long long PGmoney;
#endif
/* ----------------
* Geometric type structures
* ----------------
*/
typedef struct
{
double x;
double y;
} PGpoint;
typedef struct
{
PGpoint pts[2];
} PGlseg;
typedef struct
{
PGpoint high;
PGpoint low;
} PGbox;
typedef struct
{
PGpoint center;
double radius;
} PGcircle;
typedef struct
{
int npts;
int closed;
PGpoint *pts; /* for getf, only valid while PGresult is. */
} PGpath;
typedef struct
{
int npts;
PGpoint *pts; /* for getf, only valid while PGresult is. */
} PGpolygon;
/* ----------------
* Network type structures
* ----------------
*/
/* This struct works with CIDR as well. */
typedef struct
{
int mask;
int is_cidr;
int sa_buf_len;
/* sockaddr buffer, can be casted to sockaddr, sockaddr_in,
* sockaddr_in6 or sockaddr_stroage.
*/
char sa_buf[128];
} PGinet;
typedef struct
{
int a;
int b;
int c;
int d;
int e;
int f;
} PGmacaddr;
/* ----------------
* Date & Time structures
* ----------------
*/
typedef struct
{
int years;
int mons;
int days;
int hours;
int mins;
int secs;
int usecs;
} PGinterval;
typedef struct
{
int isbc;
int year;
int mon;
int mday;
int jday;
int yday;
int wday;
} PGdate;
typedef struct
{
int hour;
int min;
int sec;
int usec;
int withtz;
int isdst;
int gmtoff;
char tzabbr[16];
} PGtime;
typedef struct
{
PGint8 epoch;
PGdate date;
PGtime time;
} PGtimestamp;
/* ----------------
* Array structures
* ----------------
*/
#ifndef MAXDIM
# define MAXDIM 6
#endif
typedef struct
{
int ndims;
int lbound[MAXDIM];
int dims[MAXDIM];
PGparam *param;
PGresult *res;
} PGarray;
/* ----------------
* Public API funcs
* ----------------
*/
/* === in events.c === */
/* Deprecated, see PQinitTypes */
PQT_EXPORT int
PQtypesRegister(PGconn *conn);
/* === in error.c === */
PQT_EXPORT char *
PQgeterror(void);
/* PQseterror(NULL) will clear the error message */
PQT_EXPORT void
PQseterror(const char *format, ...);
/* Gets the error field for the last executed query. This only
* pertains to PQparamExec and PQparamExecPrepared. When using a
* standard libpq function like PQexec, PQresultErrorField should be used.
*/
PQT_EXPORT char *
PQgetErrorField(int fieldcode);
/* === in spec.c === */
/* Set 'format' argument to NULL to clear a single prepared specifier. */
PQT_EXPORT int
PQspecPrepare(PGconn *conn, const char *name, const char *format, int is_stmt);
PQT_EXPORT int
PQclearSpecs(PGconn *conn);
/* === in handler.c === */
/* Initialize type support on the given connection */
PQT_EXPORT int
PQinitTypes(PGconn *conn);
/* Deprecated, see PQregisterTypes */
PQT_EXPORT int
PQregisterSubClasses(PGconn *conn, PGregisterType *types, int count);
/* Deprecated, see PQregisterTypes */
PQT_EXPORT int
PQregisterComposites(PGconn *conn, PGregisterType *types, int count);
/* Deprecated, see PQregisterTypes */
PQT_EXPORT int
PQregisterUserDefinedTypes(PGconn *conn, PGregisterType *types, int count);
/* Registers PQT_SUBCLASS, PQT_COMPOSITE or PQT_USERDEFINED
* (the 'which' argument) for use with libpqtypes.
*
* For asynchronous type registration, set the 'async' argument to a
* non-zero value. This value is ignored when 'which' is PQT_SUBCLASS,
* since subclass registration does not execute any commands against the
* server. Use the standard PQconsumeInput, PQisBusy and PQgetResult
* to properly obtain a PGresult, which must be passed to PQregisterResult
* to complete the registration.
*/
PQT_EXPORT int
PQregisterTypes(PGconn *conn, int which, PGregisterType *types,
int count, int async);
/* Registers a set of 'which' types found in the given PGresult. Caller
* is responsible for clearing the result 'res'. Useful for performing
* asynchronous type registration or for caching type result data to
* avoid lookups on a new connection. If PQregisterTypes is ran in async
* mode, the PGresult obtained via PGgetResult can be cached by an
* application and provided to this function for new connections.
*
* Types and count should be identical to what was originally supplied
* to PQregisterTypes.
*
* NOTE: although a PGconn is a required argument, it is never used
* to perform any network operation (non-blocking safe).
*
* PQT_SUBCLASS is not supported and will result in an error if supplied.
*/
PQT_EXPORT int
PQregisterResult(PGconn *conn, int which, PGregisterType *types,
int count, PGresult *res);
/* Clears all type handlers registered on 'conn'. This is useful after a
* PQreset or PQresetPoll to optionally allow one to re-register types via
* PQregisterTypes.
*/
PQT_EXPORT int
PQclearTypes(PGconn *conn);
/* === in param.c === */
PQT_EXPORT PGparam *
PQparamCreate(const PGconn *conn);
PQT_EXPORT PGparam *
PQparamDup(PGparam *param);
PQT_EXPORT int
PQparamCount(PGparam *param);
PQT_EXPORT void
PQparamReset(PGparam *param);
PQT_EXPORT void
PQparamClear(PGparam *param);
PQT_EXPORT int
PQputf(PGparam *param, const char *format, ...);
PQT_EXPORT int
PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen,
const char *format, va_list ap);
/* === in exec.c === */
PQT_EXPORT int
PQgetf(const PGresult *res, int tup_num, const char *format, ...);
PQT_EXPORT int
PQgetvf(const PGresult *res, int tup_num, const char *format, va_list ap);
PQT_EXPORT PGresult *
PQexecf(PGconn *conn, const char *cmdspec, ...);
PQT_EXPORT PGresult *
PQexecvf(PGconn *conn, const char *cmdspec, va_list ap);
PQT_EXPORT int
PQsendf(PGconn *conn, const char *cmdspec, ...);
PQT_EXPORT int
PQsendvf(PGconn *conn, const char *cmdspec, va_list ap);
PQT_EXPORT PGresult *
PQparamExec(PGconn *conn, PGparam *param,
const char *command, int resultFormat);
PQT_EXPORT int
PQparamSendQuery(PGconn *conn, PGparam *param,
const char *command, int resultFormat);
PQT_EXPORT PGresult *
PQparamExecPrepared(PGconn *conn, PGparam *param,
const char *stmtName, int resultFormat);
PQT_EXPORT int
PQparamSendQueryPrepared(PGconn *conn, PGparam *param,
const char *stmtName, int resultFormat);
/* === in datetime.c === */
PQT_EXPORT void
PQlocalTZInfo(time_t *t, int *gmtoff, int *isdst, char **tzabbrp);
#ifdef __cplusplus
}
#endif
#endif /* !LIBPQTYPES_H */

View file

@ -0,0 +1,162 @@
/*
* misc.c
* Type handler for CHAR, BOOL, MONEY, UUID data types.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
int
pqt_put_char(PGtypeArgs *args)
{
*args->put.out = (PGchar) va_arg(args->ap, int);
return 1;
}
int
pqt_get_char(PGtypeArgs *args)
{
DECLVALUE(args);
PGchar *chp = va_arg(args->ap, PGchar *);
CHKGETVALS(args, chp);
*chp = *value;
return 0;
}
int
pqt_put_bool(PGtypeArgs *args)
{
*args->put.out = ((char) va_arg(args->ap, PGbool)!=0) ? 1 : 0;
return 1;
}
int
pqt_get_bool(PGtypeArgs *args)
{
DECLVALUE(args);
PGbool *boolp = va_arg(args->ap, PGbool *);
CHKGETVALS(args, boolp);
if (args->format == TEXTFMT)
*boolp = (*value == 't') ? 1 : 0;
else
*boolp = (*value)!=0 ? 1 : 0;
return 0;
}
int
pqt_put_money(PGtypeArgs *args)
{
PGmoney money = va_arg(args->ap, PGmoney);
int len = args->fmtinfo->sversion >= 80300 ? 8 : 4;
if (len == 8)
pqt_swap8(args->put.out, &money, 1);
else /* truncate: pre 8.3 server expecting a 4 byte money */
pqt_buf_putint4(args->put.out, (int) money);
return len;
}
int
pqt_get_money(PGtypeArgs *args)
{
DECLVALUE(args);
DECLLENGTH(args);
PGmoney *moneyp = va_arg(args->ap, PGmoney *);
CHKGETVALS(args, moneyp);
if (args->format == TEXTFMT)
{
char buf[64];
char c, *p = buf;
char *bufend = buf + sizeof(buf);
for (; (c = *value) != '\0' && p<bufend; ++value)
{
if (isdigit(c) || c == '-')
{
*p = c;
p++;
}
}
buf[p - buf] = 0;
if (pqt_text_to_int8(buf, moneyp) == -1)
RERR_STR2INT(args);
return 0;
}
/* 8.3 uses a 64-bit money type. Need compatibility
* for communicating with older servers.
*/
if (valuel == 4)
pqt_buf_putint4(moneyp, pqt_buf_getint4(value));
else
pqt_swap8(moneyp, value, 0);
return 0;
}
int
pqt_put_uuid(PGtypeArgs *args)
{
PGuuid uuid = va_arg(args->ap, PGuuid);
PUTNULLCHK(args, uuid);
args->put.out = uuid;
return 16;
}
int
pqt_get_uuid(PGtypeArgs *args)
{
DECLVALUE(args);
PGuuid *uuid = va_arg(args->ap, PGuuid *);
CHKGETVALS(args, uuid);
if (args->format == TEXTFMT)
{
int i;
char buf[128];
char *p = buf;
DECLLENGTH(args);
/* format: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 */
for (i=0; i < valuel; i++)
{
if (value[i] != '-')
{
*p++ = (pqt_hex_to_dec(value[i]) << 4) | pqt_hex_to_dec(value[i+1]);
i++;
}
}
*uuid = (char *) PQresultAlloc(args->get.result, p - buf);
if (!*uuid)
RERR_MEM(args);
memcpy(*uuid, buf, p - buf);
return 0;
}
*uuid = value;
return 0;
}
int
pqt_put_null(PGtypeArgs *args)
{
args->put.out = NULL;
return 0;
}

View file

@ -0,0 +1,257 @@
/*
* network.c
* Type handler for the network data types.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
#if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \
!defined(HAVE_GETADDRINFO))
# include "getaddrinfo.h"
#endif
#ifndef PGSQL_AF_INET
# define PGSQL_AF_INET (AF_INET + 0)
#endif
#ifndef PGSQL_AF_INET6
# define PGSQL_AF_INET6 (AF_INET + 1)
#endif
#ifndef AF_INET6
#warning NO AF_INET6 SUPPORT!
#endif
/* Some platforms don't define this, like AIX 4.3 */
#ifndef AI_NUMERICHOST
# define AI_NUMERICHOST 0x04
#endif
/* handles cidr as well */
int
pqt_put_inet(PGtypeArgs *args)
{
unsigned char *b = (unsigned char *)args->put.out;
PGinet *inet = va_arg(args->ap, PGinet *);
int family;
PUTNULLCHK(args, inet);
family = ((struct sockaddr *)inet->sa_buf)->sa_family;
if (family == AF_INET)
{
struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf;
*b++ = (unsigned char) PGSQL_AF_INET;
*b++ = (unsigned char) inet->mask;
*b++ = (unsigned char) (inet->is_cidr ? 1 : 0);
*b++ = (unsigned char) 4;
memcpy(b, &sa->sin_addr, 4);
b += 4;
}
#ifdef AF_INET6
else if (family == AF_INET6)
{
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf;
*b++ = (unsigned char) PGSQL_AF_INET6;
*b++ = (unsigned char) inet->mask;
*b++ = (unsigned char) (inet->is_cidr ? 1 : 0);
*b++ = (unsigned char) 16;
memcpy(b, &sa->sin6_addr, 16);
b += 16;
}
#endif
else
{
return args->errorf(args, "Unknown inet address family %d", family);
}
return (int) ((char *) b - args->put.out);
}
static int
get_inet2(PGtypeArgs *args, int is_cidr)
{
DECLVALUE(args);
unsigned short family;
PGinet *inet = va_arg(args->ap, PGinet *);
CHKGETVALS(args, inet);
if (args->format == TEXTFMT)
{
int r;
char *p;
char ipstr[80];
struct addrinfo *ai = NULL;
struct addrinfo hints;
pqt_strcpy(ipstr, sizeof(ipstr), value);
if ((p = strrchr(ipstr, '/')))
{
*p = 0;
inet->mask = atoi(p+1);
}
else
{
inet->mask = 32;
}
inet->is_cidr = is_cidr;
/* suppress hostname lookups */
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
/* Without this, windows chokes with WSAHOST_NOT_FOUND */
#ifdef PQT_WIN32
hints.ai_family = AF_INET;
#endif
if ((r = getaddrinfo(ipstr, NULL, &hints, &ai)) || !ai)
{
if(r == EAI_BADFLAGS)
{
hints.ai_flags = 0;
r = getaddrinfo(ipstr, NULL, &hints, &ai);
}
/* Another WSAHOST_NOT_FOUND work around, but for IPv6 */
#if defined(PQT_WIN32) && defined(AF_INET6)
else if(r == WSAHOST_NOT_FOUND)
{
hints.ai_flags = 0;
hints.ai_family = AF_INET6;
r = getaddrinfo(ipstr, NULL, &hints, &ai);
}
#endif
if(r)
RERR_STR2INT(args);
}
inet->sa_buf_len = (int) ai->ai_addrlen;
memcpy(inet->sa_buf, ai->ai_addr, inet->sa_buf_len);
/* Some platforms, lika AIX 4.3, do not zero this structure properly.
* The family and port are dirty, so set the first 4 bytes to 0 and
* then re-set the family. I saw "0x1002" as the first 2 bytes of
* this structure (dumb getaddrinfo), it should be "0x0002" for AF_INET.
*/
memset(inet->sa_buf, 0, 4);
((struct sockaddr *)inet->sa_buf)->sa_family = ai->ai_addr->sa_family;
/* Uninitialized memory, postgres inet/cidr types don't store this.
* Make sure its set to 0. Another AIX problem (maybe other platforms).
*/
#ifdef AF_INET6
if (ai->ai_addr->sa_family == AF_INET6)
((struct sockaddr_in6 *)inet->sa_buf)->sin6_flowinfo = 0;
#endif
freeaddrinfo(ai);
return 0;
}
family = (unsigned short) *value++;
if (family == PGSQL_AF_INET)
{
struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf;
sa->sin_family = AF_INET;
inet->mask = (unsigned char) *value++;
inet->is_cidr = *value++;
memcpy(&sa->sin_addr, value + 1, *value);
inet->sa_buf_len = (int) sizeof(struct sockaddr_in);
}
#ifdef AF_INET6
else if (family == PGSQL_AF_INET6)
{
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf;
sa->sin6_family = AF_INET6;
inet->mask = (unsigned char) *value++;
inet->is_cidr = *value++;
memcpy(&sa->sin6_addr, value + 1, *value);
inet->sa_buf_len = (int) sizeof(struct sockaddr_in6);
}
#endif
else
{
return args->errorf(args, "Unknown inet address family %d", family);
}
return 0;
}
int
pqt_get_inet(PGtypeArgs *args)
{
return get_inet2(args, 0);
}
int
pqt_get_cidr(PGtypeArgs *args)
{
return get_inet2(args, 1);
}
int
pqt_put_macaddr(PGtypeArgs *args)
{
PGmacaddr *mac = va_arg(args->ap, PGmacaddr *);
PUTNULLCHK(args, mac);
args->put.out[0] = (unsigned char) mac->a;
args->put.out[1] = (unsigned char) mac->b;
args->put.out[2] = (unsigned char) mac->c;
args->put.out[3] = (unsigned char) mac->d;
args->put.out[4] = (unsigned char) mac->e;
args->put.out[5] = (unsigned char) mac->f;
return 6;
}
int
pqt_get_macaddr(PGtypeArgs *args)
{
DECLVALUE(args);
unsigned char *p;
PGmacaddr *mac = va_arg(args->ap, PGmacaddr *);
CHKGETVALS(args, mac);
if (args->format == TEXTFMT)
{
int a,b,c,d,e,f;
int count = sscanf(value, "%x:%x:%x:%x:%x:%x", &a,&b,&c,&d,&e,&f);
if (count != 6 || (a < 0) || (a > 255) || (b < 0) || (b > 255) ||
(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
(e < 0) || (e > 255) || (f < 0) || (f > 255))
RERR_STR2INT(args);
mac->a = a;
mac->b = b;
mac->c = c;
mac->d = d;
mac->e = e;
mac->f = f;
return 0;
}
p = (unsigned char *) value;
mac->a = *p++;
mac->b = *p++;
mac->c = *p++;
mac->d = *p++;
mac->e = *p++;
mac->f = *p;
return 0;
}

View file

@ -0,0 +1,741 @@
/*
* numeric.c
* Type handler for the numeric data types.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
#include <errno.h>
/* Extremely annoying warning "conversion from 'int ' to 'short'" on
* Visual Studio 6. Normally this warning is useful but not in this
* case. Disable it.
*/
#if defined(PQT_MSVC) && PQT_MSVC <= 1200
# pragma warning (disable : 4244)
#endif
/*
* Macros and structures for receiving numeric field in binary
*/
#define NBASE 10000
#define HALF_NBASE 5000
#define DEC_DIGITS 4 /* decimal digits per NBASE digit */
#define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */
#define DIV_GUARD_DIGITS 4
/*
* Hardcoded precision limit - arbitrary, but must be small enough that
* dscale values will fit in 14 bits.
*/
#define NUMERIC_MAX_PRECISION 1000
/*
* Sign values and macros to deal with packing/unpacking n_sign_dscale
*/
#define NUMERIC_SIGN_MASK 0xC000
#define NUMERIC_POS 0x0000
#define NUMERIC_NEG 0x4000
#define NUMERIC_NAN 0xC000
#define NUMERIC_DSCALE_MASK 0x3FFF
#define NUMERIC_SIGN(n) ((n)->n_sign_dscale & NUMERIC_SIGN_MASK)
#define NUMERIC_DSCALE(n) ((n)->n_sign_dscale & NUMERIC_DSCALE_MASK)
#define NUMERIC_IS_NAN(n) (NUMERIC_SIGN(n) != NUMERIC_POS && \
NUMERIC_SIGN(n) != NUMERIC_NEG)
typedef short NumericDigit;
static const int round_powers[4] = {0, 1000, 100, 10};
typedef struct NumericVar
{
int ndigits; /* # of digits in digits[] - can be 0! */
int weight; /* weight of first digit */
int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */
int dscale; /* display scale */
NumericDigit *buf; /* start of palloc'd space for digits[] */
NumericDigit *digits; /* base-NBASE digits */
} NumericVar;
static int str2num(PGtypeArgs *args, const char *str, NumericVar *dest);
static int num2str(char *out, size_t outl, NumericVar *var, int dscale);
int
pqt_put_int2(PGtypeArgs *args)
{
PGint2 n = (PGint2) va_arg(args->ap, int);
pqt_buf_putint2(args->put.out, n);
return 2;
}
int
pqt_get_int2(PGtypeArgs *args)
{
DECLVALUE(args);
PGint2 *i2p = va_arg(args->ap, PGint2 *);
CHKGETVALS(args, i2p);
if (args->format == TEXTFMT)
{
int n;
errno = 0;
if ((n = (int) strtol(value, NULL, 10)) == 0 && errno)
RERR_STR2INT(args);
*i2p = (PGint2)n;
return 0;
}
*i2p = pqt_buf_getint2(value);
return 0;
}
/* handles oid as well */
int
pqt_put_int4(PGtypeArgs *args)
{
PGint4 n = va_arg(args->ap, PGint4);
*(PGint4 *)args->put.out = (PGint4) htonl(n);
return 4;
}
/* handles oid as well */
int
pqt_get_int4(PGtypeArgs *args)
{
DECLVALUE(args);
PGint4 *i4p = va_arg(args->ap, PGint4 *);
CHKGETVALS(args, i4p);
if (args->format == TEXTFMT)
{
PGint4 n;
/* Use strtoul so this can support Oid */
if ((n = (PGint4) strtoul(value, NULL, 10)) == 0 && errno)
RERR_STR2INT(args);
*i4p = n;
return 0;
}
*i4p = (PGint4) pqt_buf_getint4(value);
return 0;
}
int
pqt_put_int8(PGtypeArgs *args)
{
PGint8 i8 = va_arg(args->ap, PGint8);
pqt_swap8(args->put.out, &i8, 1);
return 8;
}
int
pqt_get_int8(PGtypeArgs *args)
{
DECLVALUE(args);
PGint8 *i8p = va_arg(args->ap, PGint8 *);
CHKGETVALS(args, i8p);
if (args->format == TEXTFMT)
{
if (pqt_text_to_int8(value, i8p) == -1)
RERR_STR2INT(args);
return 0;
}
pqt_swap8(i8p, value, 0);
return 0;
}
int
pqt_put_float4(PGtypeArgs *args)
{
PGfloat4 f = (PGfloat4) va_arg(args->ap, double);
void *vp = (void *)&f;
pqt_buf_putint4(args->put.out, *(int *) vp);
return 4;
}
int
pqt_get_float4(PGtypeArgs *args)
{
DECLVALUE(args);
int *f4p = (int *) va_arg(args->ap, PGfloat4 *);
CHKGETVALS(args, f4p);
if (args->format == TEXTFMT)
{
PGfloat4 f;
errno = 0;
if ((f = (PGfloat4) strtod(value, NULL)) == 0 && errno)
RERR_STR2INT(args);
*(PGfloat4 *) f4p = f;
return 0;
}
*f4p = pqt_buf_getint4(value);
return 0;
}
int
pqt_put_float8(PGtypeArgs *args)
{
PGfloat8 d = va_arg(args->ap, PGfloat8);
pqt_swap8(args->put.out, &d, 1);
return 8;
}
int
pqt_get_float8(PGtypeArgs *args)
{
DECLVALUE(args);
PGfloat8 *f8p = va_arg(args->ap, PGfloat8 *);
CHKGETVALS(args, f8p);
if (args->format == TEXTFMT)
{
if (!pqt_text_to_float8(f8p, value, NULL))
RERR_STR2INT(args);
return 0;
}
pqt_swap8(f8p, value, 0);
return 0;
}
/* exposing a NumericVar struct to a libpq user, or something similar,
* doesn't seem useful w/o a library to operate on it. Instead, we
* always expose a numeric in text format and let the API user decide
* how to use it .. like strod or a 3rd party big number library. We
* always send a numeric in binary though.
*/
int
pqt_put_numeric(PGtypeArgs *args)
{
int numlen;
NumericVar num = {0};
short *out;
PGnumeric str = va_arg(args->ap, PGnumeric);
if (str2num(args, str, &num))
{
if (num.digits)
free(num.digits);
return -1;
}
/* variable length data type, grow args->put.out buffer if needed */
numlen = (int) sizeof(short) * (4 + num.ndigits);
if (args->put.expandBuffer(args, numlen) == -1)
return -1;
out = (short *) args->put.out;
*out++ = htons((short) num.ndigits);
*out++ = htons((short) num.weight);
*out++ = htons((short) num.sign);
*out++ = htons((short) num.dscale);
if (num.digits)
{
int i;
for (i=0; i < num.ndigits; i++)
*out++ = htons(num.digits[i]);
free(num.digits);
}
return numlen;
}
/* exposing a NumericVar struct to a libpq user, or something similar,
* doesn't seem useful w/o a library to operate on it. Instead, we
* always expose a numeric in text format and let the API user decide
* how to use it .. like strod or a 3rd party big number library.
*/
int
pqt_get_numeric(PGtypeArgs *args)
{
int i;
short *s;
NumericVar num;
DECLVALUE(args);
char buf[4096];
size_t len;
PGnumeric *str = va_arg(args->ap, PGnumeric *);
CHKGETVALS(args, str);
if (args->format == TEXTFMT)
{
*str = value;
return 0;
}
s = (short *) value;
num.ndigits = ntohs(*s); s++;
num.weight = ntohs(*s); s++;
num.sign = ntohs(*s); s++;
num.dscale = ntohs(*s); s++;
num.digits = (short *) malloc(num.ndigits * sizeof(short));
if (!num.digits)
RERR_MEM(args);
for (i=0; i < num.ndigits; i++)
{
num.digits[i] = ntohs(*s);
s++;
}
i = num2str(buf, sizeof(buf), &num, num.dscale);
free(num.digits);
/* num2str failed, only fails when 'str' is too small */
if (i == -1)
RERR(args, "out buffer is too small");
len = strlen(buf)+1;
*str = PQresultAlloc(args->get.result, len);
if (!*str)
RERR_MEM(args);
memcpy(*str, buf, len);
return 0;
}
/*
* round_var
*
* Round the value of a variable to no more than rscale decimal digits
* after the decimal point. NOTE: we allow rscale < 0 here, implying
* rounding before the decimal point.
*/
static void
round_var(NumericVar *var, int rscale)
{
NumericDigit *digits = var->digits;
int di;
int ndigits;
int carry;
var->dscale = rscale;
/* decimal digits wanted */
di = (var->weight + 1) * DEC_DIGITS + rscale;
/*
* If di = 0, the value loses all digits, but could round up to 1 if its
* first extra digit is >= 5. If di < 0 the result must be 0.
*/
if (di < 0)
{
var->ndigits = 0;
var->weight = 0;
var->sign = NUMERIC_POS;
}
else
{
/* NBASE digits wanted */
ndigits = (di + DEC_DIGITS - 1) / DEC_DIGITS;
/* 0, or number of decimal digits to keep in last NBASE digit */
di %= DEC_DIGITS;
if (ndigits < var->ndigits ||
(ndigits == var->ndigits && di > 0))
{
var->ndigits = ndigits;
if (di == 0)
carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0;
else
{
/* Must round within last NBASE digit */
int extra,
pow10;
pow10 = round_powers[di];
extra = digits[--ndigits] % pow10;
digits[ndigits] = digits[ndigits] - (NumericDigit) extra;
carry = 0;
if (extra >= pow10 / 2)
{
pow10 += digits[ndigits];
if (pow10 >= NBASE)
{
pow10 -= NBASE;
carry = 1;
}
digits[ndigits] = (NumericDigit) pow10;
}
}
/* Propagate carry if needed */
while (carry)
{
carry += digits[--ndigits];
if (carry >= NBASE)
{
digits[ndigits] = (NumericDigit) (carry - NBASE);
carry = 1;
}
else
{
digits[ndigits] = (NumericDigit) carry;
carry = 0;
}
}
if (ndigits < 0)
{
var->digits--;
var->ndigits++;
var->weight++;
}
}
}
}
/*
* strip_var
*
* Strip any leading and trailing zeroes from a numeric variable
*/
static void
strip_var(NumericVar *var)
{
NumericDigit *digits = var->digits;
int ndigits = var->ndigits;
/* Strip leading zeroes */
while (ndigits > 0 && *digits == 0)
{
digits++;
var->weight--;
ndigits--;
}
/* Strip trailing zeroes */
while (ndigits > 0 && digits[ndigits - 1] == 0)
ndigits--;
/* If it's zero, normalize the sign and weight */
if (ndigits == 0)
{
var->sign = NUMERIC_POS;
var->weight = 0;
}
var->digits = digits;
var->ndigits = ndigits;
}
/*
* str2num()
*
* Parse a string and put the number into a variable
* returns -1 on error and 0 for success.
*/
static int
str2num(PGtypeArgs *args, const char *str, NumericVar *dest)
{
const char *cp = str;
int have_dp = FALSE;
int i;
unsigned char *decdigits;
int sign = NUMERIC_POS;
int dweight = -1;
int ddigits;
int dscale = 0;
int weight;
int ndigits;
int offset;
NumericDigit *digits;
/*
* We first parse the string to extract decimal digits and determine the
* correct decimal weight. Then convert to NBASE representation.
*/
/* skip leading spaces */
while (*cp)
{
if (!isspace((unsigned char) *cp))
break;
cp++;
}
switch (*cp)
{
case '+':
sign = NUMERIC_POS;
cp++;
break;
case '-':
sign = NUMERIC_NEG;
cp++;
break;
}
if (*cp == '.')
{
have_dp = TRUE;
cp++;
}
if (!isdigit((unsigned char) *cp))
return args->errorf(args,
"invalid input syntax for type numeric: '%s'", str);
decdigits = (unsigned char *) malloc(strlen(cp) + DEC_DIGITS * 2);
/* leading padding for digit alignment later */
memset(decdigits, 0, DEC_DIGITS);
i = DEC_DIGITS;
while (*cp)
{
if (isdigit((unsigned char) *cp))
{
decdigits[i++] = *cp++ - '0';
if (!have_dp)
dweight++;
else
dscale++;
}
else if (*cp == '.')
{
if (have_dp)
{
free(decdigits);
return args->errorf(args,
"invalid input syntax for type numeric: '%s'", str);
}
have_dp = TRUE;
cp++;
}
else
break;
}
ddigits = i - DEC_DIGITS;
/* trailing padding for digit alignment later */
memset(decdigits + i, 0, DEC_DIGITS - 1);
/* Handle exponent, if any */
if (*cp == 'e' || *cp == 'E')
{
long exponent;
char *endptr;
cp++;
exponent = strtol(cp, &endptr, 10);
if (endptr == cp)
{
free(decdigits);
return args->errorf(args,
"invalid input syntax for type numeric: '%s'", str);
}
cp = endptr;
if (exponent > NUMERIC_MAX_PRECISION ||
exponent < -NUMERIC_MAX_PRECISION)
{
free(decdigits);
return args->errorf(args,
"invalid input syntax for type numeric: '%s'", str);
}
dweight += (int) exponent;
dscale -= (int) exponent;
if (dscale < 0)
dscale = 0;
}
/* Should be nothing left but spaces */
while (*cp)
{
if (!isspace((unsigned char) *cp))
{
free(decdigits);
return args->errorf(args,
"invalid input syntax for type numeric: '%s'", str);
}
cp++;
}
/*
* Okay, convert pure-decimal representation to base NBASE. First we need
* to determine the converted weight and ndigits. offset is the number of
* decimal zeroes to insert before the first given digit to have a
* correctly aligned first NBASE digit.
*/
if (dweight >= 0)
weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1;
else
weight = -((-dweight - 1) / DEC_DIGITS + 1);
offset = (weight + 1) * DEC_DIGITS - (dweight + 1);
ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS;
dest->digits = (NumericDigit *) malloc((ndigits) * sizeof(NumericDigit));
dest->ndigits = ndigits;
dest->sign = sign;
dest->weight = weight;
dest->dscale = dscale;
i = DEC_DIGITS - offset;
digits = dest->digits;
while (ndigits-- > 0)
{
*digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 +
decdigits[i + 2]) * 10 + decdigits[i + 3];
i += DEC_DIGITS;
}
free(decdigits);
/* Strip any leading/trailing zeroes, and normalize weight if zero */
strip_var(dest);
return 0;
}
/*
* num2str() -
*
* Convert a var to text representation (guts of numeric_out).
* CAUTION: var's contents may be modified by rounding!
* returns -1 on error and 0 for success.
*/
static int
num2str(char *out, size_t outl, NumericVar *var, int dscale)
{
//char *str;
char *cp;
char *endcp;
int i;
int d;
NumericDigit dig;
NumericDigit d1;
if (dscale < 0)
dscale = 0;
/*
* Check if we must round up before printing the value and do so.
*/
round_var(var, dscale);
/*
* Allocate space for the result.
*
* i is set to to # of decimal digits before decimal point. dscale is the
* # of decimal digits we will print after decimal point. We may generate
* as many as DEC_DIGITS-1 excess digits at the end, and in addition we
* need room for sign, decimal point, null terminator.
*/
i = (var->weight + 1) * DEC_DIGITS;
if (i <= 0)
i = 1;
if (outl <= (size_t) (i + dscale + DEC_DIGITS + 2))
return -1;
//str = palloc(i + dscale + DEC_DIGITS + 2);
//cp = str
cp = out;
/*
* Output a dash for negative values
*/
if (var->sign == NUMERIC_NEG)
*cp++ = '-';
/*
* Output all digits before the decimal point
*/
if (var->weight < 0)
{
d = var->weight + 1;
*cp++ = '0';
}
else
{
for (d = 0; d <= var->weight; d++)
{
dig = (d < var->ndigits) ? var->digits[d] : 0;
/* In the first digit, suppress extra leading decimal zeroes */
{
int putit = (d > 0);
d1 = dig / 1000;
dig -= d1 * 1000;
putit |= (d1 > 0);
if (putit)
*cp++ = (char) (d1 + '0');
d1 = dig / 100;
dig -= d1 * 100;
putit |= (d1 > 0);
if (putit)
*cp++ = (char) (d1 + '0');
d1 = dig / 10;
dig -= d1 * 10;
putit |= (d1 > 0);
if (putit)
*cp++ = (char) (d1 + '0');
*cp++ = (char) (dig + '0');
}
}
}
/*
* If requested, output a decimal point and all the digits that follow it.
* We initially put out a multiple of DEC_DIGITS digits, then truncate if
* needed.
*/
if (dscale > 0)
{
*cp++ = '.';
endcp = cp + dscale;
for (i = 0; i < dscale; d++, i += DEC_DIGITS)
{
dig = (d >= 0 && d < var->ndigits) ? var->digits[d] : 0;
d1 = dig / 1000;
dig -= d1 * 1000;
*cp++ = (char) (d1 + '0');
d1 = dig / 100;
dig -= d1 * 100;
*cp++ = (char) (d1 + '0');
d1 = dig / 10;
dig -= d1 * 10;
*cp++ = (char) (d1 + '0');
*cp++ = (char) (dig + '0');
}
cp = endcp;
}
/*
* terminate the string and return it
*/
*cp = '\0';
return 0;
}

View file

@ -0,0 +1,484 @@
/*
* param.c
* Management functions for the PGparam object.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
static int argsExpandBuffer(PGtypeArgs *args, int new_len);
PGparam *
PQparamCreate(const PGconn *conn)
{
PGparam *param;
PGtypeData *connData;
PQseterror(NULL);
if (!conn)
{
PQseterror("PGconn cannot be NULL");
return NULL;
}
param = (PGparam *) malloc(sizeof(PGparam));
if (!param)
{
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
memset(param, 0, sizeof(PGparam));
connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc);
if (!connData)
{
PQparamClear(param);
PQseterror("No type data exists for PGconn at %p", conn);
return NULL;
}
/* copy any handlers from conn object */
if (connData->typhcnt > 0)
{
param->typhandlers = pqt_duphandlers(
connData->typhandlers, connData->typhcnt);
if (!param->typhandlers)
{
PQparamClear(param);
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
param->typhcnt = connData->typhcnt;
}
/* copy any typespecs from conn object */
if (connData->typspeccnt > 0)
{
param->typspecs = pqt_dupspecs(
connData->typspecs, connData->typspeccnt);
if (!param->typspecs)
{
PQparamClear(param);
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
param->typspeccnt = connData->typspeccnt;
}
pqt_getfmtinfo(conn, &param->fmtinfo);
return param;
}
PQT_EXPORT PGparam *
PQparamDup(PGparam *param)
{
PGparam *new_param;
PQseterror(NULL);
if (!param)
{
PQseterror("PGparam to duplicate cannot be NULL");
return NULL;
}
new_param = (PGparam *) malloc(sizeof(PGparam));
if (!new_param)
{
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
memset(new_param, 0, sizeof(PGparam));
/* copy any handlers from conn object */
if (param->typhcnt > 0)
{
new_param->typhandlers = pqt_duphandlers(
param->typhandlers, param->typhcnt);
if (!new_param->typhandlers)
{
PQparamClear(new_param);
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
new_param->typhcnt = param->typhcnt;
}
/* copy any typespecs from conn object */
if (param->typspeccnt > 0)
{
new_param->typspecs = pqt_dupspecs(
param->typspecs, param->typspeccnt);
if (!new_param->typspecs)
{
PQparamClear(new_param);
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
new_param->typspeccnt = param->typspeccnt;
}
memcpy(&new_param->fmtinfo, &param->fmtinfo, sizeof(PGtypeFormatInfo));
/* copy any values, don't bother if array is empty. */
if (param->vcnt > 0)
{
int i;
for (i=0; i < param->vcnt; i++)
{
int flags = 0;
PGvalue *val = &param->vals[i];
/* Is val->data is direct user pointer? If so, set pointer flag
* so pqt_putparam doesn't deep copy it.
*/
if (val->ptr != val->data)
flags |= TYPFLAG_POINTER;
if (!pqt_putparam(new_param, val->data, val->datal, flags,
val->format, val->oid))
{
PQparamClear(new_param);
return NULL;
}
}
}
return new_param;
}
int
PQparamCount(PGparam *param)
{
if (param)
return param->vcnt;
return 0;
}
void
PQparamReset(PGparam *param)
{
if (param)
param->vcnt = 0;
}
void
PQparamClear(PGparam *param)
{
int i;
if (!param)
return;
/* vmax is the size of the vals array. We don't use vcnt because
* someone could of just called PQparamReset, leaving vcnt 0. If
* we find any value pointers that are not NULL, free'm.
*/
for (i=0; i < param->vmax; i++)
if (param->vals[i].ptr)
free(param->vals[i].ptr);
if (param->vals)
free(param->vals);
pqt_freehandlers(param->typhandlers, param->typhcnt);
pqt_freespecs(param->typspecs, param->typspeccnt);
param->vmax = 0;
param->vcnt = 0;
param->vals = NULL;
param->typhcnt = 0;
param->typhandlers = NULL;
param->typspeccnt = 0;
param->typspecs = NULL;
free(param);
}
int
PQputf(PGparam *param, const char *format, ...)
{
int r;
va_list ap;
/* This function is just a wrapper to PQputvf */
va_start(ap, format);
r = PQputvf(param, NULL, 0, format, ap);
va_end(ap);
return r;
}
int
PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen,
const char *format, va_list ap)
{
int n=0;
int flags;
size_t stmtPos = 0;
int save_vcnt;
int typpos = 0;
PGtypeHandler *h;
PGtypeArgs args;
char args_outbuf[4096];
PGtypeSpec *spec = NULL;
PQseterror(NULL);
if (!param)
{
PQseterror("PGparam cannot be NULL");
return FALSE;
}
if (!format || !*format)
{
PQseterror("param 'format' cannot be NULL or an empty string");
return FALSE;
}
if (stmtBuf && stmtBufLen < 1)
{
PQseterror("Invalid argument: stmtBufLen must be >= 1");
return FALSE;
}
save_vcnt = param->vcnt;
va_copy(args.ap, ap);
/* "@name" format, lookup typeSpec in cache */
if(format && *format == '@')
{
spec = pqt_getspec(param->typspecs, param->typspeccnt, format + 1);
/* If we didn't find a type spec, this is an error. A format string
* with a '@' as its first character is reserved.
*/
if (!spec)
{
PQseterror("No such prepared specifier name: '%s'", format + 1);
return FALSE;
}
}
while (format && *format)
{
if (spec)
{
/* done, no more handlers in cached spec string. */
if (typpos == spec->idcnt)
break;
h = pqt_gethandlerbyid(param->typhandlers, param->typhcnt,
spec->idlist[typpos]);
/* should be an unusual, or a "will never happen", situation */
if (!h)
{
va_end(args.ap);
PQseterror("Unknown type handler id at position %d", typpos+1);
param->vcnt = save_vcnt;
return FALSE;
}
flags = (int) spec->flags[typpos];
typpos++;
}
else
{
format = pqt_parse(format, param->typhandlers, param->typhcnt,
stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags);
if (!format)
{
param->vcnt = save_vcnt;
return FALSE;
}
if (!h)
continue;
}
args.is_put = 1;
args.put.param = param;
args.fmtinfo = &param->fmtinfo;
args.put.out = args_outbuf;
args.put.__allocated_out = NULL;
args.put.outl = (int) sizeof(args_outbuf);
args.is_ptr = (flags & TYPFLAG_POINTER) ? 1 : 0;
args.format = BINARYFMT;
args.put.expandBuffer = argsExpandBuffer;
args.typpos = typpos;
args.typhandler = h;
args.errorf = pqt_argserrorf;
args.super = pqt_argssuper;
*args.put.out = 0;
if (flags & TYPFLAG_ARRAY)
n = pqt_put_array(&args);
else
n = h->typput(&args);
if (n == -1)
{
if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
free(args.put.__allocated_out);
param->vcnt = save_vcnt;
return FALSE;
}
if (args.put.out == NULL)
{
args.format = BINARYFMT;
n = -1;
}
n = pqt_putparam(param, args.put.out, n, flags, args.format,
(flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid);
if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
free(args.put.__allocated_out);
if (!n)
{
param->vcnt = save_vcnt;
return FALSE;
}
}
if (stmtBuf)
stmtBuf[stmtPos] = 0;
return TRUE;
}
/* ----------------------------
* Helper functions
*/
int
pqt_putparam(PGparam *param, const void *data, int datal,
int flags, int format, Oid typoid)
{
PGvalue *v;
if (!param)
return FALSE;
if (!data)
datal = -1;
/* need to grow param vals array */
if (param->vcnt == param->vmax)
{
PGvalue *vals;
int vmax = param->vmax ? (param->vmax * 3) / 2 : 16;
vals = (PGvalue *) pqt_realloc(param->vals, sizeof(PGvalue) * vmax);
if (!vals)
{
PQseterror(PQT_OUTOFMEMORY);
return FALSE;
}
/* zero out the new array elements */
memset(vals + param->vcnt, 0, (vmax - param->vcnt) * sizeof(PGvalue));
param->vmax = vmax;
param->vals = vals;
}
/* grab next param value */
v = &param->vals[param->vcnt];
if (datal == -1)
{
v->data = NULL;
}
/* wants to put a direct pointer */
else if (flags & TYPFLAG_POINTER)
{
v->data = (char *) data;
}
else
{
/* need more mem for param value ptr */
if (v->ptrl < datal)
{
char *ptr = (char *) pqt_realloc(v->ptr, datal);
if (!ptr)
{
PQseterror(PQT_OUTOFMEMORY);
return FALSE;
}
v->ptrl = datal;
v->ptr = ptr;
}
memcpy(v->ptr, data, datal);
v->data = v->ptr;
}
v->datal = datal;
v->format = format;
v->oid = typoid;
param->vcnt++;
return TRUE;
}
/* returns 0 on success and -1 on error */
static int
argsExpandBuffer(PGtypeArgs *args, int new_len)
{
char *new_out;
if (new_len <= args->put.outl)
return 0;
if (!args->put.__allocated_out)
{
new_out = (char *) malloc(new_len);
if (!new_out)
return args->errorf(args, PQT_OUTOFMEMORY);
/* fully copy existing stack buffer, we don't know what data
* the user has already written and may want to keep.
*/
memcpy(new_out, args->put.out, args->put.outl);
}
else
{
new_out = (char *) realloc(args->put.__allocated_out, new_len);
if (!new_out)
return args->errorf(args, PQT_OUTOFMEMORY);
}
args->put.out = args->put.__allocated_out = new_out;
args->put.outl = new_len;
return 0;
}

View file

@ -0,0 +1,587 @@
/*
* port.c
* Portability functions. Some of these functions are drop-ins for
* systems missing them and others just centralize differences.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
#if defined(__MINGW32__) || defined(__CYGWIN__)
# define HAVE_VSNPRINTF
#endif
int
pqt_snprintf(char *buf, size_t size, const char *format, ...)
{
int n;
va_list ap;
va_start(ap, format);
n = pqt_vsnprintf(buf, size, format, ap);
va_end(ap);
return n;
}
int
pqt_vsnprintf(char *buf, size_t size, const char *format, va_list ap)
{
int n;
#ifdef PQT_MSVC
# if PQT_MSVC >= 1400
/* MSVC 8 */
n = _vsnprintf_s(buf, size, size-1, format, ap);
# else
/* MSVC 6 or 7 */
n = _vsnprintf(buf, size, format, ap);
# endif
#elif defined(HAVE_VSNPRINTF)
/* All other platforms, including MinGW and Cygwin. */
n = vsnprintf(buf, size, format, ap);
#else
/* Some platforms don't have the buffer-safe version */
n = vsprintf(buf, format, ap);
#endif
if (n > -1 && (size_t) n < size)
return n;
/* Although some implementations return "required" buf size, this
* always return -1 to keep things consistent for caller.
*/
return -1;
}
#if defined(HAVE_CONFIG_H) && (!defined(HAVE_STRTOL) || \
!defined(HAVE_STRTOUL))
static unsigned long string2long(const char *nptr, char **endptr,
int base, int is_signed);
#ifndef HAVE_STRTOL
long
strtol(const char *nptr, char **endptr, int base)
{
return (signed long) string2long(nptr, endptr, base, 1);
}
#endif
#ifndef HAVE_STRTOUL
unsigned long
strtoul(const char *nptr, char **endptr, int base)
{
return (unsigned long) string2long(nptr, endptr, base, 0);
}
#endif
#define between(a, c, z) \
((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a)))
static unsigned long
string2long(const char *nptr, char ** const endptr,
int base, int is_signed)
{
unsigned int v;
unsigned long val = 0;
int c;
int ovfl = 0, sign = 1;
const char *startnptr = nptr, *nrstart;
if (endptr)
*endptr = (char *) nptr;
while (isspace(*nptr))
nptr++;
c = *nptr;
if (c == '-' || c == '+')
{
if (c == '-')
sign = -1;
nptr++;
}
nrstart = nptr; /* start of the number */
/* When base is 0, the syntax determines the actual base */
if (base == 0)
{
if (*nptr == '0')
{
if (*++nptr == 'x' || *nptr == 'X')
{
base = 16;
nptr++;
}
else
{
base = 8;
}
}
else
{
base = 10;
}
}
else if (base==16 && *nptr=='0' && (*++nptr =='x' || *nptr =='X'))
{
nptr++;
}
for (;;)
{
c = *nptr;
if (between('0', c, '9'))
v = c - '0';
else if (between('a', c, 'z'))
v = c - 'a' + 0xa;
else if (between('A', c, 'Z'))
v = c - 'A' + 0xA;
else
break;
if (v >= base)
break;
if (val > (ULONG_MAX - v) / base)
ovfl++;
val = (val * base) + v;
nptr++;
}
if (endptr)
{
if (nrstart == nptr)
*endptr = (char *) startnptr;
else
*endptr = (char *) nptr;
}
if (!ovfl)
{
/* Overflow is only possible when converting a signed long. */
if (is_signed && ((sign < 0 && val > -(unsigned long) LONG_MIN)
|| (sign > 0 && val > LONG_MAX)))
ovfl++;
}
if (ovfl)
{
errno = ERANGE;
if (is_signed)
{
if (sign < 0)
return LONG_MIN;
else
return LONG_MAX;
}
else
{
return ULONG_MAX;
}
}
return (long) sign * val;
}
#endif /* !strtol || !strtoul */
/* Non-windows platforms that don't have strtoll */
#if defined(HAVE_CONFIG_H) && !defined(HAVE_STRTOLL)
#define MIN_INT64 (-MAX_INT64 - PQT_INT64CONST(1))
#define MAX_INT64 PQT_INT64CONST(9223372036854775807)
/* no locale support */
long long int
strtoll(const char *nptr, char **endptr, int base)
{
const char *s;
/* LONGLONG */
long long int acc, cutoff;
int c;
int neg, any, cutlim;
/* endptr may be NULL */
#ifdef __GNUC__
/* This outrageous construct just to shut up a GCC warning. */
(void) &acc; (void) &cutoff;
#endif
/*
* Skip white space and pick up leading +/- sign if any.
* If base is 0, allow 0x for hex and 0 for octal, else
* assume decimal; if base is already 16, allow 0x.
*/
s = nptr;
do {
c = (unsigned char) *s++;
} while (isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else {
neg = 0;
if (c == '+')
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
/*
* Compute the cutoff value between legal numbers and illegal
* numbers. That is the largest legal value, divided by the
* base. An input number that is greater than this value, if
* followed by a legal input character, is too big. One that
* is equal to this value may be valid or not; the limit
* between valid and invalid numbers is then based on the last
* digit. For instance, if the range for long longs is
* [-9223372036854775808..9223372036854775807] and the input base
* is 10, cutoff will be set to 922337203685477580 and cutlim to
* either 7 (neg==0) or 8 (neg==1), meaning that if we have
* accumulated a value > 922337203685477580, or equal but the
* next digit is > 7 (or 8), the number is too big, and we will
* return a range error.
*
* Set any if any `digits' consumed; make it negative to indicate
* overflow.
*/
cutoff = neg ? MIN_INT64 : MAX_INT64;
cutlim = (int) (cutoff % base);
cutoff /= base;
if (neg) {
if (cutlim > 0) {
cutlim -= base;
cutoff += 1;
}
cutlim = -cutlim;
}
for (acc = 0, any = 0;; c = (unsigned char) *s++) {
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0)
continue;
if (neg) {
if (acc < cutoff || (acc == cutoff && c > cutlim)) {
any = -1;
acc = MIN_INT64;
errno = ERANGE;
} else {
any = 1;
acc *= base;
acc -= c;
}
} else {
if (acc > cutoff || (acc == cutoff && c > cutlim)) {
any = -1;
acc = MAX_INT64;
errno = ERANGE;
} else {
any = 1;
acc *= base;
acc += c;
}
}
}
if (endptr != 0)
/* LINTED interface specification */
*endptr = (char *) (any ? s - 1 : nptr);
return (acc);
}
#endif /* !HAVE_STRTOLL */
/* Non-windows machines missing getaddrinfo (postgres's port) */
#if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \
!defined(HAVE_GETADDRINFO))
#undef FRONTEND
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "getaddrinfo.h"
extern int h_errno;
/*
* get address info for ipv4 sockets.
*
* Bugs: - only one addrinfo is set even though hintp is NULL or
* ai_socktype is 0
* - AI_CANONNAME is not supported.
* - servname can only be a number, not text.
*/
int
getaddrinfo(const char *node, const char *service,
const struct addrinfo * hintp,
struct addrinfo ** res)
{
struct addrinfo *ai;
struct sockaddr_in sin,
*psin;
struct addrinfo hints;
if (hintp == NULL)
{
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
}
else
memcpy(&hints, hintp, sizeof(hints));
if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
return EAI_FAMILY;
if (hints.ai_socktype == 0)
hints.ai_socktype = SOCK_STREAM;
if (!node && !service)
return EAI_NONAME;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
if (node)
{
if (node[0] == '\0')
sin.sin_addr.s_addr = htonl(INADDR_ANY);
else if (hints.ai_flags & AI_NUMERICHOST)
{
if (!inet_aton(node, &sin.sin_addr))
return EAI_FAIL;
}
else
{
struct hostent *hp;
#ifdef FRONTEND
struct hostent hpstr;
char buf[BUFSIZ];
int herrno = 0;
pqGethostbyname(node, &hpstr, buf, sizeof(buf),
&hp, &herrno);
#else
hp = gethostbyname(node);
#endif
if (hp == NULL)
{
switch (h_errno)
{
case HOST_NOT_FOUND:
case NO_DATA:
return EAI_NONAME;
case TRY_AGAIN:
return EAI_AGAIN;
case NO_RECOVERY:
default:
return EAI_FAIL;
}
}
if (hp->h_addrtype != AF_INET)
return EAI_FAIL;
memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
}
}
else
{
if (hints.ai_flags & AI_PASSIVE)
sin.sin_addr.s_addr = htonl(INADDR_ANY);
else
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}
if (service)
sin.sin_port = htons((unsigned short) atoi(service));
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
sin.sin_len = sizeof(sin);
#endif
ai = malloc(sizeof(*ai));
if (!ai)
return EAI_MEMORY;
psin = malloc(sizeof(*psin));
if (!psin)
{
free(ai);
return EAI_MEMORY;
}
memcpy(psin, &sin, sizeof(*psin));
ai->ai_flags = 0;
ai->ai_family = AF_INET;
ai->ai_socktype = hints.ai_socktype;
ai->ai_protocol = hints.ai_protocol;
ai->ai_addrlen = sizeof(*psin);
ai->ai_addr = (struct sockaddr *) psin;
ai->ai_canonname = NULL;
ai->ai_next = NULL;
*res = ai;
return 0;
}
void
freeaddrinfo(struct addrinfo * res)
{
if (res)
{
if (res->ai_addr)
free(res->ai_addr);
free(res);
}
}
const char *
gai_strerror(int errcode)
{
#ifdef HAVE_HSTRERROR
int hcode;
switch (errcode)
{
case EAI_NONAME:
hcode = HOST_NOT_FOUND;
break;
case EAI_AGAIN:
hcode = TRY_AGAIN;
break;
case EAI_FAIL:
default:
hcode = NO_RECOVERY;
break;
}
return hstrerror(hcode);
#else /* !HAVE_HSTRERROR */
switch (errcode)
{
case EAI_NONAME:
return "Unknown host";
case EAI_AGAIN:
return "Host name lookup failure";
/* Errors below are probably WIN32 only */
#ifdef EAI_BADFLAGS
case EAI_BADFLAGS:
return "Invalid argument";
#endif
#ifdef EAI_FAMILY
case EAI_FAMILY:
return "Address family not supported";
#endif
#ifdef EAI_MEMORY
case EAI_MEMORY:
return "Not enough memory";
#endif
#ifdef EAI_NODATA
#ifndef WIN32_ONLY_COMPILER /* MSVC complains because another case has the
* same value */
case EAI_NODATA:
return "No host data of that type was found";
#endif
#endif
#ifdef EAI_SERVICE
case EAI_SERVICE:
return "Class type not found";
#endif
#ifdef EAI_SOCKTYPE
case EAI_SOCKTYPE:
return "Socket type not supported";
#endif
default:
return "Unknown server error";
}
#endif /* HAVE_HSTRERROR */
}
/*
* Convert an ipv4 address to a hostname.
*
* Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV
* It will never resolv a hostname.
* - No IPv6 support.
*/
int
getnameinfo(const struct sockaddr * sa, int salen,
char *node, int nodelen,
char *service, int servicelen, int flags)
{
/* Invalid arguments. */
if (sa == NULL || (node == NULL && service == NULL))
return EAI_FAIL;
/* We don't support those. */
if ((node && !(flags & NI_NUMERICHOST))
|| (service && !(flags & NI_NUMERICSERV)))
return EAI_FAIL;
#if defined(HAVE_IPV6) || defined(AF_INET6)
if (sa->sa_family == AF_INET6)
return EAI_FAMILY;
#endif
if (node)
{
int ret = -1;
if (sa->sa_family == AF_INET)
{
char *p;
p = inet_ntoa(((struct sockaddr_in *) sa)->sin_addr);
ret = pqt_snprintf(node, nodelen, "%s", p);
}
if (ret == -1)
return EAI_MEMORY;
}
if (service)
{
int ret = -1;
if (sa->sa_family == AF_INET)
{
ret = pqt_snprintf(service, servicelen, "%d",
ntohs(((struct sockaddr_in *) sa)->sin_port));
}
if (ret == -1)
return EAI_MEMORY;
}
return 0;
}
#endif /* HAVE_GETADDRINFO */

View file

@ -0,0 +1,153 @@
/* src/pqt_config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <arpa/inet.h> header file. */
#undef HAVE_ARPA_INET_H
/* Define to 1 if you have the `ceil' function. */
#undef HAVE_CEIL
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the `floor' function. */
#undef HAVE_FLOOR
/* Define if getaddrinfo exists */
#undef HAVE_GETADDRINFO
/* Define to 1 if you have the `hstrerror' function. */
#undef HAVE_HSTRERROR
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define to 1 if you have the <linux/limits.h> header file. */
#undef HAVE_LINUX_LIMITS_H
/* Define to 1 if you have the `localtime_r' function. */
#undef HAVE_LOCALTIME_R
/* Define to 1 if you have the <math.h> header file. */
#undef HAVE_MATH_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define to 1 if you have the <netinet/in.h> header file. */
#undef HAVE_NETINET_IN_H
/* Define to 1 if you have the `rint' function. */
#undef HAVE_RINT
/* Define to 1 if the system has the type `socklen_t'. */
#undef HAVE_SOCKLEN_T
/* Define to 1 if you have the <stddef.h> header file. */
#undef HAVE_STDDEF_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `strtol' function. */
#undef HAVE_STRTOL
/* Define to 1 if you have the `strtoll' function. */
#undef HAVE_STRTOLL
/* Define to 1 if you have the `strtoul' function. */
#undef HAVE_STRTOUL
/* Define to 1 if `ss_len' is member of `struct sockaddr_storage'. */
#undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
/* Define to 1 if `tm_gmtoff' is member of `struct tm'. */
#undef HAVE_STRUCT_TM_TM_GMTOFF
/* Define to 1 if `tm_zone' is member of `struct tm'. */
#undef HAVE_STRUCT_TM_TM_ZONE
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <time.h> header file. */
#undef HAVE_TIME_H
/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use
`HAVE_STRUCT_TM_TM_ZONE' instead. */
#undef HAVE_TM_ZONE
/* Define to 1 if you don't have `tm_zone' but do have the external array
`tzname'. */
#undef HAVE_TZNAME
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the `vsnprintf' function. */
#undef HAVE_VSNPRINTF
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if using FSU pthreads */
#undef PTHREAD_FSU
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define to 1 if architecture requires strict memory alignment */
#undef STRICT_MEMORY_ALIGNMENT
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
/* Version number of package */
#undef VERSION
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `unsigned' if <sys/types.h> does not define. */
#undef size_t

View file

@ -0,0 +1,164 @@
/*
* record.c
* Type handler for the record/composite data type.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
int
pqt_put_record(PGtypeArgs *args)
{
int i;
int len;
char *out;
PGparam *param = va_arg(args->ap, PGparam *);
PUTNULLCHK(args, param);
/* watch for invalidation issues */
if (param->vcnt > args->typhandler->nattrs)
return args->errorf(args,
"param value count is %d but server says it's %d",
param->vcnt, args->typhandler->nattrs);
/* Auto-fill the remaining fields with SQL NULLs. This feature was
* added because it was needed (by eSilo). We had a few cases where
* we needed to append new fields to existing composites but wanted
* to maintain backwards compatibility ... so some_func(mycomposite)
* would continue to work even for older versions unaware of the
* new composite fields. I guess for some this is unwanted behavior,
* but I think the cases for it are much more common.
*/
if (param->vcnt < args->typhandler->nattrs)
{
int nattrs = args->typhandler->nattrs - param->vcnt;
for (i=0; i < nattrs; i++)
pqt_putparam(param, NULL, 0, 0, BINARYFMT,
args->typhandler->attDescs[param->vcnt].attoid);
}
/* column count, 4-byte integer */
len = 4;
/* determine total byte count to ensure args->put.out is large enough */
for (i=0; i < param->vcnt; i++)
{
len += (4 + 4); /* oid + len */
if (param->vals[i].datal > 0)
len += param->vals[i].datal;
}
/* ensure out buffer is large enough */
if (args->put.expandBuffer(args, len) == -1)
return -1;
out = args->put.out;
/* write column count */
pqt_buf_putint4(out, param->vcnt);
out += 4;
for (i=0; i < param->vcnt; i++)
{
if (param->vals[i].format == 0)
return args->errorf(args,
"Cannot put composite attributes in text format");
if (param->vals[i].datal == NULL_LEN)
param->vals[i].oid = args->typhandler->attDescs[i].attoid;
/* watch for invalidation issues */
if (param->vals[i].oid != args->typhandler->attDescs[i].attoid)
return args->errorf(args,
"param value OID is %u but server says it's %u",
param->vals[i].oid, args->typhandler->attDescs[i].attoid);
/* write column oid */
pqt_buf_putint4(out, param->vals[i].oid);
out += 4;
/* write column data length */
pqt_buf_putint4(out, param->vals[i].datal);
out += 4;
/* write the column data */
if (param->vals[i].data && param->vals[i].datal > 0)
{
memcpy(out, param->vals[i].data, param->vals[i].datal);
out += param->vals[i].datal;
}
}
return len;
}
int
pqt_get_record(PGtypeArgs *args)
{
int i;
int nattrs;
int vlen;
Oid server_oid;
DECLVALUE(args);
PGresult *res;
PGresult **resultp = va_arg(args->ap, PGresult **);
CHKGETVALS(args, resultp);
if (args->format == TEXTFMT)
return args->errorf(args, "record does not support text results");
/* get record column count, numAttributes */
nattrs = pqt_buf_getint4(value);
value += 4;
/* watch for invalidation issues */
if (args->typhandler->nattrs != nattrs)
return args->errorf(args,
"type handler attribute count is %d but server says it's %d",
args->typhandler->nattrs, nattrs);
if (!(res = pqt_copyresult(args, nattrs)))
RERR_MEM(args);
for (i=0; i < nattrs; i++)
{
/* watch for invalidation issues */
server_oid = (Oid) pqt_buf_getint4(value);
if (server_oid != args->typhandler->attDescs[i].attoid)
{
args->errorf(args,
"type handler attribute OID is %u but server says it's %u",
args->typhandler->attDescs[i].attoid, server_oid);
PQclear(res);
return -1;
}
/* move past Oid */
value += 4;
/* get value length */
vlen = pqt_buf_getint4(value);
value += 4;
if (!PQsetvalue(res, 0, i, value, vlen))
{
PQclear(res);
return -1;
}
if (vlen > 0)
value += vlen;
}
*resultp = res;
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,658 @@
/*
* spec.c
* Type Specifier parser and compiler.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
/* For use with pqt_parse */
#define CHKSTMTBUF(nbytes_add) do{ \
if ((*stmtPos + (nbytes_add)) >= stmtBufLen) \
{ \
PQseterror("statement buffer is too small"); \
return FALSE; \
} \
}while (0)
/* For use with PQspecPrepare */
#define FREESTMTBUF do{ \
if (stmtBuf && stmtBuf != buffer) \
free(stmtBuf); \
}while(0)
static char *
skipQuotes(char *s);
static char *
parseId(char *id, char **start, int *len, int *flags, int typpos);
static int
expandSpecs(PGtypeData *typeData);
int
PQspecPrepare(PGconn *conn, const char *name,
const char *format, int is_stmt)
{
int flags;
int typpos = 0;
int idmax = 0;
size_t stmtPos = 0;
PGtypeHandler *h;
PGtypeData *typeData;
PGtypeSpec *spec;
size_t stmtBufLen = 0;
char *stmtBuf = NULL;
char buffer[8192];
if (!conn)
{
PQseterror("PGConn cannot be NULL");
return FALSE;
}
if (!name || !*name)
{
PQseterror("Prepared specifier name cannot be NULL or an empty string");
return FALSE;
}
if (format && !*format)
{
PQseterror("Specifier format string cannot be empty");
return FALSE;
}
if (!isalnum(*name) && *name != '_')
{
PQseterror("Prepared specifier name must begin with an alpha, "
"number or underscore.");
return FALSE;
}
typeData = PQinstanceData(conn, pqt_eventproc);
if (!typeData)
{
PQseterror("No type data exists for PGconn at %p", conn);
return FALSE;
}
/* This is a removal request */
if (!format)
{
int i;
for (i=0; i < typeData->typspeccnt; i++)
{
if (strcmp(typeData->typspecs[i].name, name) == 0)
{
/* clear it */
pqt_clearspec(&typeData->typspecs[i]);
/* remove from list, not needed if its the last element */
if (i != typeData->typspeccnt - 1)
memmove(typeData->typspecs + i, typeData->typspecs + i + 1,
(typeData->typspeccnt - i - 1) * sizeof(PGtypeSpec));
typeData->typspeccnt--;
break;
}
}
/* always return TRUE, an error is not useful here */
return TRUE;
}
/* Already exists case */
spec = pqt_getspec(typeData->typspecs, typeData->typspeccnt, name);
if (spec)
{
PQseterror("Prepared spec already exists '%s'", name);
return FALSE;
}
/* Make sure specs array is large enough */
if (!expandSpecs(typeData))
return FALSE;
spec = &typeData->typspecs[typeData->typspeccnt];
/* cache statement along with prepared type spec */
if (is_stmt)
{
stmtBufLen = strlen(format) + 1;
/* no room in stack, use heap */
if (stmtBufLen > sizeof(buffer))
{
stmtBuf = (char *) malloc(stmtBufLen);
if (!stmtBuf)
{
PQseterror(PQT_OUTOFMEMORY);
return FALSE;
}
}
else
{
stmtBuf = buffer;
stmtBufLen = sizeof(buffer);
}
}
while (format && *format)
{
format = pqt_parse(format, typeData->typhandlers, typeData->typhcnt,
stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags);
if (!format)
{
pqt_clearspec(spec);
FREESTMTBUF;
return FALSE;
}
/* skipped harmless chars in format, like quoted sections. */
if(!h)
continue;
if (!spec->idlist || spec->idcnt == idmax)
{
int c = idmax ? idmax * 2 : 8;
void *p = pqt_realloc(spec->idlist, c * sizeof(int));
if (!p)
{
PQseterror(PQT_OUTOFMEMORY);
pqt_clearspec(spec);
FREESTMTBUF;
return FALSE;
}
spec->idlist = (int *) p;
p = pqt_realloc(spec->flags, c * sizeof(char));
if (!p)
{
PQseterror(PQT_OUTOFMEMORY);
pqt_clearspec(spec);
FREESTMTBUF;
return FALSE;
}
spec->flags = (unsigned char *) p;
idmax = c;
}
/* Parallel arrays, every handler needs type flags */
spec->idlist[spec->idcnt] = h->id;
spec->flags[spec->idcnt++] = (unsigned char) flags;
}
/* terminate stmtBuf, guarenteed to have room for NUL */
if (stmtBuf)
stmtBuf[stmtPos] = 0;
/* copy name string */
spec->name = strdup(name);
if (!spec->name)
{
pqt_clearspec(spec);
PQseterror(PQT_OUTOFMEMORY);
FREESTMTBUF;
return FALSE;
}
/* copy the parameterized stmt string */
if (stmtBuf)
{
spec->stmt = strdup(stmtBuf);
if (!spec->stmt)
{
pqt_clearspec(spec);
PQseterror(PQT_OUTOFMEMORY);
FREESTMTBUF;
return FALSE;
}
}
FREESTMTBUF;
/* Success, increment type spec count */
typeData->typspeccnt++;
return TRUE;
}
int
PQclearSpecs(PGconn *conn)
{
PGtypeData *typeData;
if (!conn)
{
PQseterror("PGConn cannot be NULL");
return FALSE;
}
typeData = PQinstanceData(conn, pqt_eventproc);
if (!typeData)
{
PQseterror("No type data exists for PGconn at %p", conn);
return FALSE;
}
pqt_freespecs(typeData->typspecs, typeData->typspeccnt);
typeData->typspecs = NULL;
typeData->typspeccnt = 0;
typeData->typspecmax = 0;
return TRUE;
}
char *pqt_parse(const char *format, PGtypeHandler *h, int hcnt,
char *stmtBuf, size_t stmtBufLen, PGtypeHandler **out, size_t *stmtPos,
int *typpos, int *flags)
{
int specMark;
char *s = skipQuotes((char *) format);
char typname[PQT_MAXIDLEN + 1];
char schema[PQT_MAXIDLEN + 1];
char tmp[200];
*out = NULL;
if (!s)
return NULL;
/* found quotes to skip */
if (s != format)
{
if (stmtBuf)
{
size_t n = s - format;
CHKSTMTBUF(n);
memcpy(stmtBuf + *stmtPos, format, n);
(*stmtPos) += n;
}
return s;
}
specMark = *format;
if (specMark != '%' && specMark != '#')
{
if (stmtBuf)
{
CHKSTMTBUF(1);
stmtBuf[*stmtPos] = *format;
(*stmtPos)++;
}
format++;
return (char *) format;
}
/* spec skips % or # */
if (!(s = pqt_parsetype(format + 1, schema, typname, flags, *typpos + 1)))
return NULL;
if (*flags & TYPFLAG_INVALID)
{
if (stmtBuf)
{
CHKSTMTBUF(1);
stmtBuf[*stmtPos] = *format++;
(*stmtPos)++;
PQseterror(NULL); /* set by pqt_parsetype */
return (char *) format;
}
return NULL;
}
(*typpos)++;
if (!(*out = pqt_gethandler(h, hcnt, schema, typname)))
{
PQseterror("Uknown type '%s' (position %d)",
pqt_fqtn(tmp, sizeof(tmp), schema, typname), *typpos);
return NULL;
}
if (stmtBuf)
{
int n = pqt_snprintf(tmp, sizeof(tmp), "$%d", *typpos);
CHKSTMTBUF(n);
memcpy(stmtBuf + *stmtPos, tmp, n);
(*stmtPos) += n;
}
if (!(*out)->typput)
{
PGtypeHandler *o = pqt_gethandlerbyid(h, hcnt, h->base_id);
if (!o || !o->typput)
{
PQseterror(
"Type '%s' doesn't support put operations (position %d)",
pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema,
(*out)->typname), *typpos);
*out = NULL;
return NULL;
}
*out = o;
}
if ((*flags & TYPFLAG_POINTER) && !pqt_allowsptr(*out))
{
PQseterror(
"Type '%s' doesn't support putting pointers (position %d)",
pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema,
(*out)->typname), *typpos);
*out = NULL;
return NULL;
}
if (specMark == '#')
(*flags) |= TYPFLAG_BYNAME;
return s;
}
void
pqt_clearspec(PGtypeSpec *spec)
{
if (spec->name)
free(spec->name);
if (spec->stmt)
free(spec->stmt);
if (spec->idlist)
free(spec->idlist);
if (spec->flags)
free(spec->flags);
memset(spec, 0, sizeof(PGtypeSpec));
}
PGtypeSpec *pqt_dupspecs(PGtypeSpec *specs, int count)
{
int i;
PGtypeSpec *new_specs = (PGtypeSpec *) malloc(count * sizeof(PGtypeSpec));
if (!new_specs)
return NULL;
memset(new_specs, 0, count * sizeof(PGtypeSpec));
for (i=0; i < count; i++)
{
PGtypeSpec *s = &specs[i];
PGtypeSpec *news = &new_specs[i];
news->idcnt = s->idcnt;
news->name = strdup(s->name);
if (!news->name)
{
pqt_freespecs(new_specs, i+1);
return NULL;
}
if(s->stmt)
{
news->stmt = strdup(s->stmt);
if (!news->stmt)
{
pqt_freespecs(new_specs, i+1);
return NULL;
}
}
news->idlist = (int *) malloc(s->idcnt * sizeof(int));
if (!news->idlist)
{
pqt_freespecs(new_specs, i+1);
return NULL;
}
memcpy(news->idlist, s->idlist, s->idcnt * sizeof(int));
news->flags = (unsigned char *) malloc(s->idcnt * sizeof(char));
if (!news->flags)
{
pqt_freespecs(new_specs, i+1);
return NULL;
}
memcpy(news->flags, s->flags, s->idcnt * sizeof(char));
}
return new_specs;
}
void pqt_freespecs(PGtypeSpec *specs, int count)
{
int i;
for (i=0; i < count; i++)
pqt_clearspec(&specs[i]);
if (specs)
free(specs);
}
PGtypeSpec *pqt_getspec(PGtypeSpec *specs, int count, const char *name)
{
int i;
for (i=0; i < count; i++)
if (strcmp(specs[i].name, name) == 0)
return &specs[i];
return NULL;
}
/* Parse a type identifer name (schema qualified or not) from spec. spec
* must point to the first char after the % sign, which maybe a
* double quote.
*
* spec - pointer to typname, just after the '%' or '#'
* schema - buffer to receive schema (PQT_MAXIDLEN bytes)
* typname - buffer to receive typname (PQT_MAXIDLEN bytes)
* flags - a pointer to an int that is set one or more TYPFLAG_xxx
* typpos - 1-based position of spec in specifier string (0 for unknown)
*/
char *
pqt_parsetype(const char *spec, char *schema, char *typname,
int *flags, int typpos)
{
int i;
char *start;
int len=0;
char *s = (char *)spec;
if (!(s = parseId(s, &start, &len, flags, typpos)))
return NULL;
/* not a valid specifer, false positive like "(x % y) = 0" */
if (*flags & TYPFLAG_INVALID)
return s;
*schema = 0;
if (*s == '.')
{
memcpy(schema, start, len);
schema[len] = 0;
if (*flags & TYPFLAG_CASEFOLD)
for (i=0; i < len; i++)
schema[i] = pqt_tolower(schema[i]);
/* now get typname */
if (!(s = parseId(++s, &start, &len, flags, typpos)))
return NULL;
if (*flags & TYPFLAG_INVALID)
return s;
}
memcpy(typname, start, len);
typname[len] = 0;
if (*flags & TYPFLAG_CASEFOLD)
for (i=0; i < len; i++)
typname[i] = pqt_tolower(typname[i]);
return s;
}
static char *
parseId(char *id, char **start, int *len, int *flags, int typpos)
{
char *p = id;
*flags = 0;
*start = NULL;
*len = 0;
if (*p == '"')
p++;
/* check first character */
if (!isalpha(*p) && *p != '_')
{
*flags |= TYPFLAG_INVALID;
PQseterror(
"Invalid first character for identifier '%c' (pos:%d)", *p, typpos);
return p;
}
if (*id == '"')
{
id++;
if (!(p = strchr(id, '"')))
{
*flags |= TYPFLAG_INVALID;
PQseterror("Unterminated double quote '%s' (pos:%d)",
id-1, typpos);
return p;
}
*len = (int) (p - id);
*start = id;
p++;
}
else
{
for (p=id+1; isalnum(*p) || *p=='_'; p++) ;
*len = (int) (p - id);
*start = id;
*flags |= TYPFLAG_CASEFOLD;
}
/* range check */
if (*len == 0 || *len > PQT_MAXIDLEN)
{
*flags |= TYPFLAG_INVALID;
PQseterror("Identifier out of range %d (pos:%d), range is 1 to %d",
*len, typpos, PQT_MAXIDLEN);
return p;
}
/* direct pointer request */
if (*p == '*')
{
p++;
*flags |= TYPFLAG_POINTER;
}
/* Is this an array? Ex. %int4[] or %"a b"[] */
if (p[0] == '[' && p[1] == ']')
{
if (*flags & TYPFLAG_POINTER)
{
PQseterror(
"'*' specifer flag cannot be used with arrays[] '%s' (pos:%d)",
id, typpos);
return NULL;
}
*flags |= TYPFLAG_ARRAY;
p += 2;
}
return p;
}
static int
expandSpecs(PGtypeData *typeData)
{
int n;
PGtypeSpec *specs;
if (typeData->typspeccnt < typeData->typspecmax)
return TRUE;
n = typeData->typspecmax ? (typeData->typspecmax * 3) / 2 : 8;
specs = (PGtypeSpec *) pqt_realloc(
typeData->typspecs, sizeof(PGtypeSpec) * n);
if (!specs)
{
PQseterror(PQT_OUTOFMEMORY);
return FALSE;
}
memset(specs + typeData->typspeccnt, 0,
(n - typeData->typspeccnt) * sizeof(PGtypeSpec));
typeData->typspecs = specs;
typeData->typspecmax = n;
return TRUE;
}
/* skip quoted strings. Doesn't need to account for E'' syntax. The
* E is copied over prior to the quoted string.
*
* Returns a pointer to the next character after the closing quote or
* NULL if there was an error.
*/
static char *
skipQuotes(char *s)
{
char *end;
if (*s != '\'')
return s;
end = s;
while (*++end)
{
/* If we see a backslash, skip an extra char. No need to dig any
* further since this method works with \digits and \hex.
*/
if (*end == '\\')
end++;
else if (*end == '\'')
break;
}
/* unterminated quote */
if (!*end)
{
PQseterror("unterminated single quoted string");
return NULL;
}
return ++end; /* skip ending quote */
}

View file

@ -0,0 +1,265 @@
/*
* util.c
* Utility functions.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
PGresult *
pqt_copyresult(PGtypeArgs *args, int nattrs)
{
int i;
PGresult *res;
int tableid, columnid, format;
PGresAttDesc *ad = (PGresAttDesc *) malloc(nattrs * sizeof(PGresAttDesc));
if (!ad)
{
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
tableid = PQftable(args->get.result, args->get.field_num);
columnid = PQftablecol(args->get.result, args->get.field_num);
format = PQfformat(args->get.result, args->get.field_num);
for (i=0; i < nattrs; i++)
{
ad[i].tableid = tableid;
ad[i].columnid = columnid;
ad[i].format = format;
/* simple array */
if (args->typhandler->nattrs == 0)
{
ad[i].typid = args->typhandler->typoid;
ad[i].typlen = args->typhandler->typlen;
ad[i].name = NULL;
ad[i].atttypmod = -1;
}
/* composite/record */
else
{
ad[i].typid = args->typhandler->attDescs[i].attoid;
ad[i].typlen = args->typhandler->attDescs[i].attlen;
ad[i].name = args->typhandler->attDescs[i].attname;
ad[i].atttypmod = args->typhandler->attDescs[i].atttypmod;
}
}
res = PQcopyResult(args->get.result,
PG_COPYRES_EVENTS | PG_COPYRES_NOTICEHOOKS);
if (!res)
{
free(ad);
PQseterror(PQT_OUTOFMEMORY);
return NULL;
}
if (!PQsetResultAttrs(res, nattrs, ad))
{
PQclear(res);
PQseterror(PQT_OUTOFMEMORY);
res = NULL;
}
free(ad);
return res;
}
#ifdef STRICT_MEMORY_ALIGNMENT
short
pqt_buf_getint2(char *buffer)
{
short n;
memcpy(&n, buffer, 2);
return (short) ntohs(n);
}
int
pqt_buf_getint4(char *buffer)
{
int n;
memcpy(&n, buffer, 4);
return (int) ntohl(n);
}
#endif
void
pqt_swap8(void *outp, void *inp, int tonet)
{
static int n = 1;
#ifdef STRICT_MEMORY_ALIGNMENT
unsigned int in[2];
unsigned int out[2];
memcpy(&in, inp, 8);
#else
unsigned int *in = (unsigned int *) inp;
unsigned int *out = (unsigned int *) outp;
#endif
/* swap when needed */
if (*(char *)&n == 1)
{
out[0] = (unsigned int) (tonet ? htonl(in[1]) : ntohl(in[1]));
out[1] = (unsigned int) (tonet ? htonl(in[0]) : ntohl(in[0]));
}
else
{
out[0] = in[0];
out[1] = in[1];
}
#ifdef STRICT_MEMORY_ALIGNMENT
memcpy(outp, out, 8);
#endif
}
int
pqt_text_to_int8(char *val, void *out)
{
PGint8 n;
/* NOTE: port version of strtoll in port.c. */
errno = 0;
if ((n = (PGint8) strtoll(val, NULL, 10)) == 0 && errno)
return -1;
*(PGint8 *) out = n;
return 0;
}
int
pqt_text_to_float8(double *f8, char *text, char **endptr)
{
double d;
errno = 0;
if ((d = strtod(text, endptr)) == 0 && errno)
return 0;
*f8 = d;
return 1;
}
/* Checks buffer and truncates 'src' if 'dest' is too small. */
char *
pqt_strcpy(char *dest, size_t size, const char *src)
{
size_t src_len = strlen(src);
/* truncate if needed */
if (src_len >= size)
src_len = size - 1;
memcpy(dest, src, src_len);
dest[src_len] = 0;
return dest;
}
/* ---------------------------
* Everything below was taken from postgresql project
* A couple of changes here and there.
*/
#ifndef HIGHBIT
# define HIGHBIT (0x80)
#endif
#ifndef IS_HIGHBIT_SET
# define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT)
#endif
/*
* Fold a character to lower case.
*
* Unlike some versions of tolower(), this is safe to apply to characters
* that aren't upper case letters. Note however that the whole thing is
* a bit bogus for multibyte character sets.
*/
unsigned char
pqt_tolower(unsigned char ch)
{
if (ch >= 'A' && ch <= 'Z')
ch += 'a' - 'A';
else if (IS_HIGHBIT_SET(ch) && isupper(ch))
ch = (unsigned char) tolower(ch);
return ch;
}
/*
* Case-independent comparison of two null-terminated strings.
*/
int
pqt_strcasecmp(const char *s1, const char *s2)
{
for (;;)
{
unsigned char ch1 = (unsigned char) *s1++;
unsigned char ch2 = (unsigned char) *s2++;
if (ch1 != ch2)
{
if (ch1 >= 'A' && ch1 <= 'Z')
ch1 += 'a' - 'A';
else if (IS_HIGHBIT_SET(ch1) && isupper(ch1))
ch1 = (unsigned char)tolower(ch1);
if (ch2 >= 'A' && ch2 <= 'Z')
ch2 += 'a' - 'A';
else if (IS_HIGHBIT_SET(ch2) && isupper(ch2))
ch2 = (unsigned char)tolower(ch2);
if (ch1 != ch2)
return (int) ch1 - (int) ch2;
}
if (ch1 == 0)
break;
}
return 0;
}
/*
* Case-independent comparison of two not-necessarily-null-terminated
* strings. At most n bytes will be examined from each string.
*/
int
pqt_strncasecmp(const char *s1, const char *s2, size_t n)
{
while (n-- > 0)
{
unsigned char ch1 = (unsigned char) *s1++;
unsigned char ch2 = (unsigned char) *s2++;
if (ch1 != ch2)
{
if (ch1 >= 'A' && ch1 <= 'Z')
ch1 += 'a' - 'A';
else if (IS_HIGHBIT_SET(ch1) && isupper(ch1))
ch1 = (unsigned char)tolower(ch1);
if (ch2 >= 'A' && ch2 <= 'Z')
ch2 += 'a' - 'A';
else if (IS_HIGHBIT_SET(ch2) && isupper(ch2))
ch2 = (unsigned char)tolower(ch2);
if (ch1 != ch2)
return (int) ch1 - (int) ch2;
}
if (ch1 == 0)
break;
}
return 0;
}

View file

@ -0,0 +1,88 @@
/*
* varlena.c
* Type handler for the variable length data types.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
/* put a type in its string representation, text format */
int
pqt_put_str(PGtypeArgs *args)
{
args->format = TEXTFMT;
args->put.out = va_arg(args->ap, char *);
return args->put.out ? (int)strlen(args->put.out)+1 : 0;
}
/* varchar, bpchar and name use the text handlers */
int
pqt_put_text(PGtypeArgs *args)
{
args->put.out = va_arg(args->ap, PGtext);
return args->put.out ? (int)strlen(args->put.out) : 0;
}
/* varchar, bpchar and name use the text handlers */
int
pqt_get_text(PGtypeArgs *args)
{
DECLVALUE(args);
PGtext *textp = va_arg(args->ap, PGtext *);
CHKGETVALS(args, textp);
*textp = value;
return 0;
}
int
pqt_put_bytea(PGtypeArgs *args)
{
PGbytea *bytea = va_arg(args->ap, PGbytea *);
PUTNULLCHK(args, bytea);
args->put.out = bytea->data;
return bytea->len;
}
int
pqt_get_bytea(PGtypeArgs *args)
{
DECLVALUE(args);
DECLLENGTH(args);
PGbytea *bytea = va_arg(args->ap, PGbytea *);
CHKGETVALS(args, bytea);
if (args->format == TEXTFMT)
{
size_t len = 0;
value = (char *) PQunescapeBytea((const unsigned char *) value, &len);
if (!value)
RERR_STR2INT(args);
bytea->data = (char *) PQresultAlloc(args->get.result, len);
if (!bytea->data)
{
PQfreemem(value);
RERR_MEM(args);
}
bytea->len = (int) len;
memcpy(bytea->data, value, len);
PQfreemem(value);
return 0;
}
/* binary format */
bytea->len = valuel;
bytea->data = value;
return 0;
}

View file

@ -0,0 +1,66 @@
##############################################################
# Project: libpqtypes
# Makefile for Microsoft Visual Studio 6, 7 or 8
#
# nmake -f win32.mak [options] [targets]
#
# For further build instructions, see the package's INSTALL file.
#
# Authors: Andrew Chernow, Merlin Moncure
# Contact: libpqtypes@esilo.com
##############################################################
PROJNAME = libpqtypes
OBJECTS = src\array.obj src\datetime.obj src\error.obj \
src\events.obj src\exec.obj src\geo.obj src\handler.obj \
src\misc.obj src\network.obj src\numerics.obj \
src\param.obj src\port.obj src\record.obj src\spec.obj \
src\utils.obj src\varlena.obj
CFLAGS = $(CFLAGS) /nologo /W4 /MD /GF /Ob2 /O2 /Oi- /D_WIN32_WINNT=0x0501
!IFDEF MT
CFLAGS = $(CFLAGS) /DPQT_THREAD_SAFE
!ENDIF
!IFDEF PQT_LONG_LONG
CFLAGS = $(CFLAGS) /DPQT_LONG_LONG=$(PQT_LONG_LONG)
!ENDIF
# set the libpath for libpq.dll and libpqdll.lib (same thing as gcc -L)
!IFDEF LPATH
LIBPATH = /LIBPATH:$(LPATH)
!ENDIF
INC2 = $(INC) /Isrc
LIBS = ws2_32.lib libpqdll.lib
LINKOPTS = /nologo /subsystem:windows /incremental:no
LINKIGNORE = /IGNORE:4089 /IGNORE:4006 /IGNORE:4068
LIBOPTS = /nologo /subsystem:windows $(LINKIGNORE)
all: $(OBJECTS)
lib $(LIBOPTS) /OUT:$(PROJNAME).lib $(OBJECTS)
link $(LINKOPTS) /dll $(OBJECTS) /out:$(PROJNAME).dll \
/IMPLIB:$(PROJNAME)dll.lib $(LIBS) $(LIBPATH)
test:
$(CC) $(CFLAGS) $(INC2) src\regression-test.c /Feregtest.exe \
$(LIBS) $(PROJNAME)dll.lib /link $(LIBPATH) $(LINKIGNORE)
-@del regression-test.obj
$(OBJECTS):
$(CC) $(CFLAGS) $(INC2) /c $** /Fo$*.obj
clean:
-@del /Q /F \
$(OBJECTS) \
$(PROJNAME).dll \
$(PROJNAME).dll.manifest \
$(PROJNAME).lib \
$(PROJNAME)dll.lib \
$(PROJNAME)dll.exp \
regtest.exe \
regtest.exe.manifest 2>NUL 1>NUL

View file

@ -6,6 +6,10 @@ add_library(${PROJECT_NAME} STATIC
resultset.cpp
)
target_include_directories(${PROJECT_NAME}
PRIVATE ${pqtypes_prefix}/include
)
target_link_libraries(${PROJECT_NAME}
PRIVATE ${bare_name}-if
PRIVATE pqtypes
)