├── README.md ├── automation └── build_libpqtypes.bat ├── bin ├── glnxa64 │ ├── libpqtypes.so │ └── libpqtypes.so.0 ├── win32 │ ├── libpqtypes.dll │ ├── libpqtypes.lib │ └── libpqtypesdll.lib └── win64 │ ├── libpqtypes.dll │ ├── libpqtypes.lib │ └── libpqtypesdll.lib └── source ├── AUTHORS ├── ChangeLog ├── INSTALL ├── LICENSE ├── Makefile.am ├── Makefile.win32 ├── README ├── acinclude.m4 ├── configure.ac ├── docs └── man3 │ ├── PQclearSpecs.3 │ ├── PQclearTypes.3 │ ├── PQexecf.3 │ ├── PQexecvf.3 │ ├── PQgetErrorField.3 │ ├── PQgeterror.3 │ ├── PQgetf.3 │ ├── PQgetvf.3 │ ├── PQinitTypes.3 │ ├── PQlocalTZInfo.3 │ ├── PQparamClear.3 │ ├── PQparamCount.3 │ ├── PQparamCreate.3 │ ├── PQparamDup.3 │ ├── PQparamExec.3 │ ├── PQparamExecPrepared.3 │ ├── PQparamReset.3 │ ├── PQparamSendQuery.3 │ ├── PQparamSendQueryPrepared.3 │ ├── PQputf.3 │ ├── PQputvf.3 │ ├── PQregisterComposites.3 │ ├── PQregisterResult.3 │ ├── PQregisterSubClasses.3 │ ├── PQregisterTypes.3 │ ├── PQregisterUserDefinedTypes.3 │ ├── PQsendf.3 │ ├── PQsendvf.3 │ ├── PQseterror.3 │ ├── PQspecPrepare.3 │ ├── PQtypesRegister.3 │ ├── pqt-composites.3 │ ├── pqt-handlers.3 │ └── pqt-specs.3 ├── groff2html ├── latest.diff ├── latest.log ├── reconf ├── src ├── array.c ├── datetime.c ├── error.c ├── events.c ├── exec.c ├── geo.c ├── getaddrinfo.h ├── handler.c ├── libpqtypes-int.h ├── libpqtypes.h ├── misc.c ├── network.c ├── numerics.c ├── param.c ├── port.c ├── record.c ├── regression-test.c ├── spec.c ├── utils.c └── varlena.c ├── win32.mak └── win32_d.mak /README.md: -------------------------------------------------------------------------------- 1 | # libpqtypes 2 | 3 | This is a fork of the official libpqtypes repository once maintained by Andrew Chernow and Merlin Moncure from eSilo [(see http://libpqtypes.esilo.com/)](http://libpqtypes.esilo.com/). 4 | The reason for creating this fork is a slow reaction from libpqtypes developers to some newly found bugs. 5 | 6 | Change Log: 7 | 8 | v 1.5.2a (most of the changes below are made by Dmitry Epstein) 9 | 10 | - PQseterror (error.c) - an optimization. 11 | - pqt_put_float4 (numerics.c) - looks like a fix for a non-standard syntax. 12 | - pqt_get_numeric (numerics.c) - a fix for a method that returns Numeric in native format. 13 | - strip_var (numerics.c) - a fix for an internal method that normalizes a string representation of a number. 14 | - str2num (numerics.c) - adds NaN handling in an internal method used for putting numbers. 15 | - num2str (numerics.c) - adds NaN handling in an internal method used for getting numbers. 16 | - pqt_parse (spec.c) - a fix for a format string parser, something to do with custom handlers, IIRC. 17 | 18 | 19 | v 1.5.2 (official unreleased version -see http://libpqtypes.esilo.com/pkgdocs.html?file=ChangeLog) 20 | 21 | -------------------------------------------------------------------------------- /automation/build_libpqtypes.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: Builds libpqtypes libraries 3 | :: 4 | :: Set the path to the local Visual C++ installation in the variable below 5 | 6 | setlocal EnableDelayedExpansion 7 | :: Visual C++ installation directory 8 | set msvcDefaultDir=C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\ 9 | set msvcDir=%~1 10 | if "%msvcDir%"=="" set msvcDir=!msvcDefaultDir! 11 | set sourceDir=%~dp0source\ 12 | set win32Dir=%~dp0win32\ 13 | set win64Dir=%~dp0win64\ 14 | set curPath=%PATH% 15 | set curDir=%cd% 16 | 17 | if not exist "%sourceDir%" ( 18 | echo Source directory not found 19 | exist /b 1 20 | ) 21 | 22 | :: find the latest version of libpq 23 | for /d %%d in (%~dp0..\..\libpq\*) do set libpqDir=%%d 24 | 25 | cd "%sourceDir%" 26 | 27 | echo Building 32-bit libpqtypes 28 | call "%msvcDir%\vcvarsall.bat" x86 29 | nmake -f win32.mak clean 30 | nmake -f win32.mak INC="-I%libpqDir%\include" LPATH="%libpqDir%\win32" all 31 | if not exist "%win32Dir%" mkdir "%win32Dir%" 32 | copy /Y *.lib "%win32Dir%" 33 | copy /Y *.dll "%win32Dir%" 34 | set PATH=%curPath% 35 | 36 | echo Building 64-bit libpqtypes 37 | call "%msvcDir%\vcvarsall.bat" x64 38 | nmake -f win32.mak clean 39 | nmake -f win32.mak INC="-I%libpqDir%\include" LPATH="%libpqDir%\win64" all 40 | if not exist "%win64Dir%" mkdir "%win64Dir%" 41 | copy /Y *.lib "%win64Dir%" 42 | copy /Y *.dll "%win64Dir%" 43 | 44 | cd "%curDir%" 45 | -------------------------------------------------------------------------------- /bin/glnxa64/libpqtypes.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/glnxa64/libpqtypes.so -------------------------------------------------------------------------------- /bin/glnxa64/libpqtypes.so.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/glnxa64/libpqtypes.so.0 -------------------------------------------------------------------------------- /bin/win32/libpqtypes.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/win32/libpqtypes.dll -------------------------------------------------------------------------------- /bin/win32/libpqtypes.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/win32/libpqtypes.lib -------------------------------------------------------------------------------- /bin/win32/libpqtypesdll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/win32/libpqtypesdll.lib -------------------------------------------------------------------------------- /bin/win64/libpqtypes.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/win64/libpqtypes.dll -------------------------------------------------------------------------------- /bin/win64/libpqtypes.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/win64/libpqtypes.lib -------------------------------------------------------------------------------- /bin/win64/libpqtypesdll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgagarinov/libpqtypes/d096a85c3269f0b2e3ab8806ff6d313aad6e2fbf/bin/win64/libpqtypesdll.lib -------------------------------------------------------------------------------- /source/AUTHORS: -------------------------------------------------------------------------------- 1 | 2 | LIBPQTYPES LIBRARY 3 | ------------------ 4 | 5 | Distributed by: 6 | 7 | eSilo, LLC. (see LICENSE file) 8 | 9 | Maintainers: 10 | People who currently fix bugs and perform most of the forward development. 11 | 12 | Andrew Chernow achernow (at) gmail (dot) com 13 | Merlin Moncure mmoncure (at) gmail (dot) com 14 | 15 | Written by: 16 | Original people behind the libpqtypes project core functionality and 17 | architecture (June 2007 - Apr 2008). The wounded warriors who battled the 18 | PostgreSQL team trying to get libpqtypes into the main source tree. That 19 | battle continues to be waged as we believe libpqtypes is a far more 20 | advanced and powerful client interface for PostgreSQL than the vanilla libpq. 21 | 22 | Andrew Chernow achernow (at) gmail (dot) com 23 | Merlin Moncure mmoncure (at) gmail (dot) com 24 | 25 | Contributors: 26 | People who have proven to be helpful over the years with their critical 27 | thinking, design ideas and bug detection. 28 | 29 | Jeremy Smith jeremy (at) esilo (dot) com 30 | Dmitry Epstein depstein (at) alliedtesting (dot) com 31 | 32 | > Last updated 2015-02-27 33 | 34 | -------------------------------------------------------------------------------- /source/INSTALL: -------------------------------------------------------------------------------- 1 | 2 | LIBPQTYPES INSTALLTION GUIDE 3 | ---------------------------- 4 | 5 | *) UNIX BUILDS 6 | 7 | For quick builds, run the below: 8 | 9 | ]# ./configure 10 | ]# make 11 | ]# make install 12 | 13 | (Also `make uninstall', which removes all installed headers, 14 | libs and mans pages) 15 | 16 | By default, only a shared library is built. To build a static 17 | library, use the `--enable-static' option. 18 | 19 | To build a thread-safe version, use the `--enable-thread-safety' option. 20 | To use libpqtypes in a thread-safe environment, the system must have 21 | pthreads installed. Most systems will have /usr/include/pthread.h 22 | and /usr/lib/libpthread.so or /usr/lib/libthread.so. 23 | 24 | The default prefix is `/usr/local', so if the `--prefix' option is 25 | not specified the header file(s) will be installed at 26 | /usr/local/include and the library file(s) will be installed at 27 | /usr/local/lib. 28 | 29 | If your system doesn't include a `long long' data type, resulting in 30 | a library compile error, you can specify an alternative by setting 31 | PQT_LONG_LONG=data_type during configure. 32 | 33 | ]# CFLAGS="-DPQT_LONG_LONG=my_int8" ./configure 34 | 35 | Files installed: 36 | 37 | - $(prefix)/include/libpqtypes.h 38 | - $(prefix)/lib/libpqtypes.so 39 | 40 | When `--enable-static' is specified: 41 | 42 | - $(prefix)/lib/libpqtypes.a 43 | 44 | To dynamically link with libpqtypes, pass -lpqtypes to the linker. 45 | 46 | 47 | 48 | *) MINGW AND CYGWIN BUILDS 49 | 50 | There is no configure for MinGW or Cygwin. Execute the below from 51 | the root of the source tar ball within the cygwin shell or msys. 52 | 53 | ]# make -f Makefile.win32 [options] [targets] 54 | 55 | 56 | 57 | *) MSVC BUILDS 58 | 59 | MSVC versions 6, 7 and 8 have all been tested. Versions prior to 6 60 | may work but are not supported. 61 | 62 | The MSVC build uses `nmake' and can be executed from the root of the 63 | source tar ball at a DOS prompt: 64 | 65 | > nmake -f win32.mak [options] [targets] 66 | 67 | 68 | 69 | *) MINGW, CYGWIN & MSVC BUILDS: 70 | 71 | For the below examples, MAKE is defined as: 72 | 73 | # MSVC 74 | MAKE = nmake -f win32.mak 75 | 76 | # CYGWIN & MINGW 77 | MAKE = make -f Makefile.win32 78 | 79 | Targets: 80 | 81 | all - build the libpqtypes library 82 | test - build the libpqtypes regression test 83 | clean - delete all files generated by compiles 84 | install - Install the binaries, headers and man3 pages 85 | uninstall - Uninstalls libpqtypes, reverse of install target 86 | 87 | Thread-safe: 88 | 89 | $(MAKE) MT=1 90 | 91 | NOTE: MinGW and/or Cygwin may require installing pthreads. 92 | 93 | Specify alternative 64-bit int data type: 94 | 95 | $(MAKE) PQT_LONG_LONG=my_int8 96 | 97 | Global make & nmake compiler/linker variables: 98 | 99 | CC = C compiler 100 | INC = includes 101 | CFLAGS = compiler flags 102 | LPATH = library path. MINGW and CYGWIN use -L while MSVC can supply a single path 103 | 104 | # If libpq is not installed and you use MSVC, try this (replace paths): 105 | > $(MAKE) INC="-Ic:\pgsql\src\interfaces\libpq" LPATH="-Ic:\pgsql\src\interfaces\libpq\Release" 106 | 107 | Files generated: 108 | 109 | libpqtypes.dll - Windows DLL 110 | libpqtypesdll.lib - Import lib for DLL 111 | libpqtypes.lib - Static library 112 | 113 | Where to install files: 114 | 115 | - System Include Path: libpqtypes.h 116 | - System Library Path: libpqtypes.dll 117 | - System Library Path: libpqtypesdll.lib 118 | - System Library Path: libpqtypes.lib (static lib) 119 | 120 | -------------------------------------------------------------------------------- /source/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | LIBPQTYPES 3 | Software extension to the PostgreSQL libpq interface. 4 | 5 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 6 | 7 | Permission to use, copy, modify, and distribute this software and its 8 | documentation for any purpose, without fee, and without a written agreement 9 | is hereby granted, provided that the above copyright notice and this 10 | paragraph and the following two paragraphs appear in all copies. 11 | 12 | IN NO EVENT SHALL ESILO, LLC. BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 13 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, 14 | ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 15 | ESILO, LLC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | 17 | ESILO, LLC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, 20 | AND ESILO, LLC. HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 21 | ENHANCEMENTS, OR MODIFICATIONS. 22 | 23 | -------------------------------------------------------------------------------- /source/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_LDFLAGS = 2 | AUTOMAKE_OPTIONS = foreign 3 | @SET_MAKE@ 4 | 5 | srcfiles = src/array.c src/datetime.c src/error.c \ 6 | src/events.c src/exec.c src/geo.c src/handler.c \ 7 | src/misc.c src/network.c src/numerics.c src/param.c \ 8 | src/port.c src/record.c src/spec.c src/utils.c src/varlena.c \ 9 | src/libpqtypes.h src/libpqtypes-int.h 10 | 11 | manpages = docs/man3/PQgeterror.3 docs/man3/PQgetf.3 \ 12 | docs/man3/PQlocalTZInfo.3 docs/man3/PQparamClear.3 \ 13 | docs/man3/PQparamCreate.3 docs/man3/PQparamExec.3 \ 14 | docs/man3/PQparamExecPrepared.3 docs/man3/PQparamReset.3 \ 15 | docs/man3/PQparamSendQuery.3 docs/man3/PQparamSendQueryPrepared.3 \ 16 | docs/man3/PQputf.3 docs/man3/PQputvf.3 \ 17 | docs/man3/PQseterror.3 docs/man3/pqt-composites.3 docs/man3/pqt-handlers.3 \ 18 | docs/man3/PQgetErrorField.3 docs/man3/pqt-specs.3 \ 19 | docs/man3/PQtypesRegister.3 docs/man3/PQparamCount.3 \ 20 | docs/man3/PQexecf.3 docs/man3/PQexecvf.3 docs/man3/PQspecPrepare.3 \ 21 | docs/man3/PQsendf.3 docs/man3/PQsendvf.3 docs/man3/PQgetvf.3 \ 22 | docs/man3/PQregisterComposites.3 docs/man3/PQregisterUserDefinedTypes.3 \ 23 | docs/man3/PQregisterSubClasses.3 docs/man3/PQregisterTypes.3 \ 24 | docs/man3/PQregisterResult.3 docs/man3/PQinitTypes.3 \ 25 | docs/man3/PQclearTypes.3 docs/man3/PQclearSpecs.3 26 | 27 | lib_LTLIBRARIES = libpqtypes.la 28 | libpqtypes_la_SOURCES = $(srcfiles) 29 | libpqtypes_la_LDFLAGS = -version-info $(LIBPQTYPES_SO_VERSION) 30 | include_HEADERS = src/libpqtypes.h 31 | dist_man_MANS = $(manpages) 32 | noinst = src/libpqtypes-int.h src/getaddrinfo.h 33 | 34 | EXTRA_DIST = install-sh src/regression-test.c src/getaddrinfo.h \ 35 | win32.mak groff2html Makefile.win32 \ 36 | AUTHORS ChangeLog INSTALL LICENSE README 37 | 38 | .PHONY: test 39 | test: 40 | $(CC) $(CFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H \ 41 | -o test src/regression-test.c $(LDFLAGS) $(LIBS) -lpq -lpqtypes 42 | @rm -f regression-test.o 43 | 44 | .PHONY: docs 45 | docs: 46 | @find docs/man3 -name '*.3' -type f | xargs -i ./groff2html \{\} 47 | 48 | uninstall: 49 | rm -f ${prefix}/include/libpqtypes.h 50 | rm -f ${prefix}/lib/libpqtypes.* 51 | @if [ 1 ] ; then \ 52 | _manpath=`man -w PQgetf`; \ 53 | if test ! -z $$_manpath ; then \ 54 | manpath=`dirname $$_manpath`; \ 55 | for man in $(manpages); do \ 56 | man=`basename $$man`; \ 57 | if test -f $${manpath}/$$man ; then \ 58 | echo rm -f $${manpath}/$$man; \ 59 | rm -f $${manpath}/$$man; \ 60 | fi; \ 61 | done \ 62 | fi; \ 63 | fi; 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /source/Makefile.win32: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # Project: libpqtypes 3 | # Makefile for Cygwin or Mingw Environments (GCC) 4 | # 5 | # make -f Makefile.win32 [options] [targets] 6 | # 7 | # For further build instructions, see the package's INSTALL file. 8 | # 9 | # Authors: Andrew Chernow, Merlin Moncure 10 | # Contact: libpqtypes@esilo.com 11 | ############################################################## 12 | 13 | PROJNAME = libpqtypes 14 | 15 | DEPS = src/libpqtypes-int.h src/libpqtypes.h 16 | OBJECTS = src/array.o src/datetime.o src/error.o \ 17 | src/events.o src/exec.o src/geo.o src/handler.o \ 18 | src/misc.o src/network.o src/numerics.o \ 19 | src/param.o src/port.o src/record.o src/spec.c \ 20 | src/utils.o src/varlena.o 21 | 22 | INC += -Isrc 23 | LIBS = -lpq -lws2_32 24 | CFLAGS += -s -Wall -Wpointer-arith -D_GNU_SOURCE -O3 \ 25 | -std=gnu99 -Wlong-long -D_WIN32_WINNT=0x0501 -D_REENTRANT 26 | 27 | ifdef MT 28 | CFLAGS += -DPQT_THREAD_SAFE -D_THREAD_SAFE 29 | LIBS += -lpthread 30 | endif 31 | 32 | ifdef PQT_LONG_LONG 33 | CFLAGS += -DPQT_LONG_LONG=$(PQT_LONG_LONG) 34 | endif 35 | 36 | all: $(OBJECTS) 37 | dllwrap -o $(PROJNAME).dll -dllname $(PROJNAME).dll $(OBJECTS) $(LPATH) $(LIBS) 38 | dlltool --dllname $(PROJNAME).dll --output-lib $(PROJNAME).a 39 | 40 | test: 41 | gcc $(CFLAGS) $(INC) -o regtest src/regression-test.c $(PROJNAME).dll $(LPATH) $(LIBS) 42 | -@rm -f regression-test.o 43 | 44 | %.o: %.c $(DEPS) 45 | gcc $(CFLAGS) $(INC) -o $@ -c $< 46 | 47 | clean: 48 | -@rm -f $(OBJECTS) $(PROJNAME).so $(PROJNAME).a regtest 49 | 50 | -------------------------------------------------------------------------------- /source/README: -------------------------------------------------------------------------------- 1 | 2 | LIBPQTYPES LIBRARY 3 | ------------------ 4 | 5 | This package contains the source code, documentation and management 6 | files for the libpqtypes library, an extension to the PostgreSQL 7 | libpq library. 8 | 9 | libpqtypes is a libpq extension that offers a new way of handling 10 | parameterized queries and getting result field values. Both 11 | putting parameters and getting values use a printf/scanf style 12 | interface, with consistent specifiers for both. 13 | 14 | - Full support for binary and text format (parameter and result) 15 | 16 | - Full support for composites, arrays and composite arrays. 17 | 18 | - printf style interface to libpq's binary parameterized API. 19 | 20 | - scanf style interface for getting values: PQgetvalue extension. 21 | 22 | - Ability to register user-defined types, aliases and data type 23 | sub-classes for use with printf style interfaces: ex. "%mytype". 24 | 25 | - Per-thread global error message: PQgeterror, PQseterror. 26 | 27 | - Online docs as well as man pages. 28 | 29 | INSTALL 30 | libpqtypes cannot be used by itself, it can only be used with libpq. 31 | To build and install libpqtypes, see the INSTALL file included with 32 | this package. 33 | 34 | LICENSE 35 | libpqtypes is released under the BSD license just like all software 36 | components of the PostgreSQL Database Management System. See the 37 | LICENSE file included with this package. 38 | 39 | AUTHORS 40 | libpqtypes is a contribution of eSilo, LLC and was written by 41 | Andrew Chernow and Merlin Moncure. See the AUTHORS file. 42 | 43 | To report bugs, feature requests or general questions goto the 44 | libpqtypes forum at http://pgfoundry.org/forum/?group_id=1000370 45 | or send an email to . 46 | 47 | Online documentation: 48 | 49 | http://libpqtypes.esilo.com/ 50 | 51 | The latest version: 52 | 53 | http://pgfoundry.org/projects/libpqtypes/ 54 | 55 | -------------------------------------------------------------------------------- /source/acinclude.m4: -------------------------------------------------------------------------------- 1 | AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl 2 | AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl 3 | AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl 4 | AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], 5 | VAR,[VAR="no, unknown" 6 | AC_LANG_SAVE 7 | AC_LANG_C 8 | ac_save_[]FLAGS="$[]FLAGS" 9 | for ac_arg dnl 10 | in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC 11 | "-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC 12 | # 13 | do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` 14 | AC_TRY_COMPILE([],[return 0;], 15 | [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) 16 | done 17 | FLAGS="$ac_save_[]FLAGS" 18 | AC_LANG_RESTORE 19 | ]) 20 | case ".$VAR" in 21 | .ok|.ok,*) m4_ifvaln($3,$3) ;; 22 | .|.no|.no,*) m4_ifvaln($4,$4) ;; 23 | *) m4_ifvaln($3,$3,[ 24 | if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null 25 | then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) 26 | else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) 27 | m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" 28 | fi ]) ;; 29 | esac 30 | AS_VAR_POPDEF([VAR])dnl 31 | AS_VAR_POPDEF([FLAGS])dnl 32 | ]) 33 | 34 | 35 | dnl the only difference - the LANG selection... and the default FLAGS 36 | 37 | AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_NEW], [dnl 38 | AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl 39 | AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$1])dnl 40 | AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)], 41 | VAR,[VAR="no, unknown" 42 | AC_LANG_SAVE 43 | AC_LANG_CPLUSPLUS 44 | ac_save_[]FLAGS="$[]FLAGS" 45 | for ac_arg dnl 46 | in "-pedantic -Werror % m4_ifval($1,$1,-option)" dnl GCC 47 | "-pedantic % m4_ifval($1,$1,-option) %% no, obsolete" dnl new GCC 48 | # 49 | do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` 50 | AC_TRY_COMPILE([],[return 0;], 51 | [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) 52 | done 53 | FLAGS="$ac_save_[]FLAGS" 54 | AC_LANG_RESTORE 55 | ]) 56 | case ".$VAR" in 57 | .ok|.ok,*) m4_ifvaln($3,$3) ;; 58 | .|.no|.no,*) m4_ifvaln($4,$4) ;; 59 | *) m4_ifvaln($3,$3,[ 60 | if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null 61 | then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR]) 62 | else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"]) 63 | m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR" 64 | fi ]) ;; 65 | esac 66 | AS_VAR_POPDEF([VAR])dnl 67 | AS_VAR_POPDEF([FLAGS])dnl 68 | ]) 69 | 70 | AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, 71 | [AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])]) 72 | 73 | AC_DEFUN([AX_CXXFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1, 74 | [AX_CXXFLAGS_GCC_OPTION_NEW($@)],[AX_CXXFLAGS_GCC_OPTION_OLD($@)])]) 75 | 76 | AC_DEFUN([AC_C_LONG_LONG], 77 | [AC_CACHE_CHECK(for long long int, ac_cv_c_long_long, 78 | [if test "$GCC" = yes; then 79 | ac_cv_c_long_long=yes 80 | else 81 | AC_TRY_COMPILE(,[long long int i;], 82 | ac_cv_c_long_long=yes, 83 | ac_cv_c_long_long=no) 84 | fi]) 85 | if test $ac_cv_c_long_long = yes; then 86 | AC_DEFINE(HAVE_LONG_LONG, 1, [compiler understands long long]) 87 | fi 88 | ]) 89 | 90 | dnl AC_CHECK_FUNC_IN(HEADER, FUNCTION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) 91 | AC_DEFUN([AC_CHECK_FUNC_IN], 92 | [AC_MSG_CHECKING([for $2 in $1]) 93 | AC_CACHE_VAL(ac_cv_func_$2, 94 | [AC_TRY_LINK( 95 | dnl Don't include because on OSF/1 3.0 it includes 96 | dnl which includes which contains a prototype for 97 | dnl select. Similarly for bzero. 98 | [/* System header to define __stub macros and hopefully few prototypes, 99 | which can conflict with char $2(); below. */ 100 | #include 101 | #include <$1> 102 | /* Override any gcc2 internal prototype to avoid an error. */ 103 | ]ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus 104 | extern "C" 105 | #endif 106 | ])dnl 107 | [/* We use char because int might match the return type of a gcc2 108 | builtin and then its argument prototype would still apply. */ 109 | char $2(); 110 | ], [ 111 | /* The GNU C library defines this for functions which it implements 112 | to always fail with ENOSYS. Some functions are actually named 113 | something starting with __ and the normal name is an alias. */ 114 | #if defined (__stub_$2) || defined (__stub___$2) 115 | choke me 116 | #else 117 | $2(); 118 | #endif 119 | ], eval "ac_cv_func_$2=yes", eval "ac_cv_func_$2=no")]) 120 | if eval "test \"`echo '$ac_cv_func_'$2`\" = yes"; then 121 | AC_MSG_RESULT(yes) 122 | ifelse([$3], , :, [$3]) 123 | else 124 | AC_MSG_RESULT(no) 125 | ifelse([$4], , , [$4 126 | ])dnl 127 | fi 128 | ]) 129 | 130 | dnl AC_CHECK_FUNCS_IN(HEADER, FUNCTION... [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) 131 | AC_DEFUN([AC_CHECK_FUNCS_IN], 132 | [for ac_func in $2 133 | do 134 | AC_CHECK_FUNC_IN($1, $ac_func, 135 | ac_tr_func=HAVE_`echo $ac_func | sed -e 'y:abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:' -e 's:[[^A-Z0-9]]:_:g'` 136 | AC_DEFINE_UNQUOTED($ac_tr_func) $3], $4)dnl 137 | done 138 | ]) 139 | 140 | -------------------------------------------------------------------------------- /source/configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.57) 5 | AC_INIT(libpqtypes, 1.5.2a, libpqtypes@esilo.com) 6 | AC_CANONICAL_TARGET 7 | AC_PROG_MAKE_SET 8 | SET_MAKE="MAKE=make --no-print-directory" 9 | AC_CONFIG_AUX_DIR([.]) 10 | AM_INIT_AUTOMAKE(libpqtypes,1.5.1) 11 | AC_SUBST([LIBPQTYPES_SO_VERSION], [1:5:1]) 12 | AC_DISABLE_STATIC 13 | AC_PROG_LIBTOOL 14 | AC_PROG_INSTALL 15 | AC_CONFIG_SRCDIR([src/param.c]) 16 | AM_CONFIG_HEADER([src/pqt_config.h:src/pqt_config.h.in]) 17 | AC_PROG_CXX 18 | AC_PROG_CC 19 | 20 | CPPFLAGS="$CPPFLAGS -Isrc" 21 | if test "$CC" = "gcc" ; then 22 | # always want reentrant funcs, not just thread-safe mode. -s 23 | CFLAGS="$CFLAGS -O2 -Wall -Wpointer-arith -D_GNU_SOURCE -D_REENTRANT -fsigned-char" 24 | 25 | # gcc doesn't indicate an error for unknown options when they are 26 | # not warning/feature options, -Wxxx or -fxxx. We have to test manually. 27 | cprog="int main(void){return 0;}" 28 | gcctest="gcc -x c -c - -o /dev/null" 29 | 30 | AC_MSG_CHECKING(CFLAGS for gcc -std=gnu99...) 31 | result=`echo "$cprog" | $gcctest -std=gnu99 2>&1` 32 | if test -z "$result" ; then 33 | CFLAGS="$CFLAGS -std=gnu99" 34 | AC_MSG_RESULT(-std=gnu99) 35 | else 36 | AC_MSG_RESULT([no, unknown]) 37 | fi 38 | 39 | # turn on as many warnings as possible 40 | AX_CFLAGS_GCC_OPTION([-Wclobbered]) 41 | AX_CFLAGS_GCC_OPTION([-Wempty-body]) 42 | AX_CFLAGS_GCC_OPTION([-Wignored-qualifiers]) 43 | #AX_CFLAGS_GCC_OPTION([-Wmissing-field-initializers]) # annoying 44 | AX_CFLAGS_GCC_OPTION([-Wmissing-parameter-type]) 45 | AX_CFLAGS_GCC_OPTION([-Wold-style-declaration]) 46 | AX_CFLAGS_GCC_OPTION([-Woverride-init]) 47 | AX_CFLAGS_GCC_OPTION([-Wsign-compare]) 48 | AX_CFLAGS_GCC_OPTION([-Wtype-limits]) 49 | AX_CFLAGS_GCC_OPTION([-Wuninitialized]) 50 | AX_CFLAGS_GCC_OPTION([-fomit-frame-pointer]) 51 | AX_CFLAGS_GCC_OPTION([-fno-strict-aliasing]) 52 | AX_CFLAGS_GCC_OPTION([-funroll-all-loops]) 53 | AX_CFLAGS_GCC_OPTION([-funit-at-a-time]) 54 | else 55 | AC_MSG_ERROR([Using $CC instead of gcc, currently not supported]) 56 | fi 57 | 58 | # Each case has an example of what target_os contains 59 | case ${target_os} in 60 | # solaris 61 | solaris*) 62 | CFLAGS="$CFLAGS -D_STDC_C99" 63 | ;; 64 | 65 | # SCO UnixWare 66 | *UnixWare*|*unixware*) 67 | CFLAGS="$CFLAGS -D__UnixWare__" 68 | ;; 69 | 70 | # SCO OpenServer 71 | sco*) 72 | CFLAGS="$CFLAGS -D__OpenServer__" 73 | ;; 74 | esac 75 | 76 | AC_ARG_ENABLE(thread-safety, [ --enable-thread-safety Enable threads], 77 | [have_threads=yes], [have_threads=no]) 78 | 79 | if test "$have_threads" = "yes" ; then 80 | AC_SEARCH_LIBS(pthread_create, [pthread thread],, 81 | [AC_MSG_ERROR([Missing libpthread.so or libthread.so, cannot use --enable-thread-safety])]) 82 | CFLAGS="$CFLAGS -DPQT_THREAD_SAFE -D_THREAD_SAFE" 83 | 84 | # SCO OpenServer 5, possibly other platforms, requires using FSU pthreads. 85 | # We detect this by looking at the prototype for pthread_getspecific. 86 | # There are a few other prototypes that are different as well but detecting 87 | # one is plenty. 88 | AC_MSG_CHECKING(whether FSU pthreads is being used) 89 | AC_TRY_COMPILE( 90 | [#include ], 91 | [pthread_getspecific((pthread_key_t)(0), (void *)(0))], 92 | [AC_DEFINE(PTHREAD_FSU, 1, 93 | [Define to 1 if using FSU pthreads]) AC_MSG_RESULT(yes)], 94 | [AC_MSG_RESULT(no)] 95 | ) 96 | fi 97 | 98 | # If strict memory alignment is required, this test will produce a 99 | # Bus error. Most riscs CPUs (old and modern) are strict. 100 | save_CFLAGS=$CFLAGS 101 | CFLAGS="" 102 | AC_MSG_CHECKING(if strict memory alignment is required) 103 | AC_RUN_IFELSE([int main(void){char b[[8]];int*i=(int*)(b+1);*i=0;return 0;}], 104 | [AC_MSG_RESULT(no)], [AC_DEFINE(STRICT_MEMORY_ALIGNMENT, 1, 105 | [Define to 1 if architecture requires strict memory alignment]) AC_MSG_RESULT(yes)] 106 | ) 107 | CFLAGS=$save_CFLAGS 108 | 109 | AC_HEADER_STDC 110 | AC_CHECK_HEADERS([ \ 111 | arpa/inet.h limits.h linux/limits.h netdb.h netinet/in.h stddef.h \ 112 | sys/socket.h time.h math.h sys/time.h strings.h]) 113 | 114 | AC_STRUCT_TIMEZONE 115 | AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,, \ 116 | [#ifdef HAVE_TIME_H 117 | #include 118 | #endif 119 | #ifdef HAVE_SYS_TIME_H 120 | #include 121 | #endif]) 122 | 123 | AC_CHECK_TYPES(socklen_t,,, 124 | [#include 125 | #ifdef HAVE_APRA_INET_H 126 | #include 127 | #endif 128 | #ifdef HAVE_SYS_SOCKET_H 129 | #include 130 | #endif]) 131 | 132 | AC_CHECK_MEMBERS([struct sockaddr_storage.ss_len],,, \ 133 | [#ifdef HAVE_SYS_SOCKET_H 134 | #include 135 | #endif 136 | #ifdef HAVE_NETINET_IN_H 137 | #include 138 | #endif]) 139 | 140 | AC_C_CONST 141 | AC_FUNC_MEMCMP 142 | AC_FUNC_STRTOD 143 | AC_HEADER_TIME 144 | AC_TYPE_SIZE_T 145 | 146 | AC_SEARCH_LIBS(pow, m) 147 | AC_SEARCH_LIBS(getaddrinfo, [nsl socket], \ 148 | [AC_DEFINE([HAVE_GETADDRINFO], [], \ 149 | [Define if getaddrinfo exists])], [], []) 150 | 151 | AC_CHECK_FUNCS([vsnprintf floor ceil rint hstrerror \ 152 | localtime_r strtol strtoll strtoul]) 153 | 154 | AC_OUTPUT(Makefile) 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /source/docs/man3/PQclearSpecs.3: -------------------------------------------------------------------------------- 1 | .so man3/PQspecPrepare.3 -------------------------------------------------------------------------------- /source/docs/man3/PQclearTypes.3: -------------------------------------------------------------------------------- 1 | .so man3/PQinitTypes.3 2 | -------------------------------------------------------------------------------- /source/docs/man3/PQexecf.3: -------------------------------------------------------------------------------- 1 | .TH "PQexecf" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQexecf \- Prepares parameters and executes a command. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | PGresult *PQexecf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, ...); 10 | .br 11 | PGresult *PQexecvf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, va_list \fIap\fP); 12 | .br 13 | int PQsendf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, ...); 14 | .br 15 | int PQsendvf(const PGconn *\fIconn\fP, const char *\fIcmd\fP, va_list \fIap\fP); 16 | \fP 17 | .SH DESCRIPTION 18 | .LP 19 | The \fIPQexecf\fP() function executes a command that uses libpqtypes 20 | type specifiers instead of $1, $2, etc... syntax. The idea 21 | is to combine \fIPQputvf\fP() and \fIPQparamExec\fP() into a single call. 22 | The variable argument list must match the type specs listed within the 23 | cmd. The type specifiers should be placed where one would normally place 24 | $1, $2, etc... 25 | 26 | The \fIPQexecvf\fP() function is identical to \fIPQexecf\fP() except 27 | it takes a va_list. 28 | 29 | The \fIPQsendf\fP() and \fIPQsendvf\fP() functions are identical to 30 | \fIPQexecf\fP() and \fIPQexecvf\fP() except they are asynchronous versions, 31 | meaning an additional call, PQgetResult, must be issued to get the PGresult 32 | object. For more information, see the PostgreSQL Documentation: specifically 33 | the "Asynchronous Command Processing" section under 34 | "Client Interfaces | libpq - C library". 35 | 36 | A prepared type spec "@name" can be used with these functions, granted that 37 | \fIPQspecPrepare\fP() was called with a non-zero value for the is_stmt 38 | argument. These functions needs an actual SQL statement to execute. 39 | .SH RETURN VALUE 40 | .LP 41 | PQexecf and PQexecvf return NULL on error and a valid PGresult on success. 42 | PQsendf and PQsendvf return zero on error and a non-zero value on success. 43 | On error, use \fIPQgeterror(3)\fP to obtain an error message. 44 | .SH EXAMPLES 45 | .LP 46 | .SS Using PQexecf 47 | The example uses PQexecf function to execute a query. 48 | .RS 49 | .nf 50 | .LP 51 | \fB 52 | /* The PQexecf call is shorthand for: 53 | * 54 | * PGparam *param = PQparamCreate(conn); 55 | * PQputf(param, "%int4 %int4", 1, 1); 56 | * res = PQparamExec(conn, param, "SELECT $1 + $2", 1); 57 | * PQparamClear(param); 58 | * 59 | * As you may notice, PQexecf makes life much simpler. 60 | */ 61 | PGresult *res = PQexecf(conn, "SELECT %int4 + %int4", 1, 1); 62 | 63 | if(!res) 64 | fprintf(stderr, "PQexecf failed: %s", PQgeterror()); 65 | else 66 | PQclear(res); 67 | 68 | /* A bit more common, this puts an int4 and a text into a generated 69 | * PGparam and then executes 'myfunc($1, $2)' 70 | */ 71 | res = PQexecf(conn, "SELECT * FROM myfunc(%int4, %text)", 2, "abc"); 72 | 73 | /* Prepared type spec example. To use with execf, the final "is_stmt" 74 | * argument must be set to a non-zero value! 75 | */ 76 | PQspecPrepare(conn, "account_insert", "INSERT INTO account VALUES " 77 | "(%int8, %text, %int4)", 1); 78 | 79 | PGint8 acc_id = 78236; 80 | PGtext acc_name = "ABC Coders, LLC."; 81 | PGint4 acc_biz = ACC_BIZ_TECHNOLOGY; 82 | 83 | PQexecf(conn, "@account_insert", acc_id, acc_name, acc_biz); 84 | \fP 85 | .fi 86 | .RE 87 | .SH AUTHOR 88 | .LP 89 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 90 | Written by Andrew Chernow and Merlin Moncure. 91 | .SH REPORTING BUGS 92 | .LP 93 | Report bugs to . 94 | .SH COPYRIGHT 95 | .LP 96 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 97 | .br 98 | This is free software; see the source for copying conditions. 99 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 100 | FOR A PARTICULAR PURPOSE. 101 | .SH SEE ALSO 102 | .LP 103 | \fIPQgeterror(3)\fP, \fIPQputvf(3)\fP, \fIPQparamExec(3)\fP 104 | 105 | -------------------------------------------------------------------------------- /source/docs/man3/PQexecvf.3: -------------------------------------------------------------------------------- 1 | .so man3/PQexecf.3 -------------------------------------------------------------------------------- /source/docs/man3/PQgetErrorField.3: -------------------------------------------------------------------------------- 1 | .so man3/PQgeterror.3 -------------------------------------------------------------------------------- /source/docs/man3/PQgeterror.3: -------------------------------------------------------------------------------- 1 | .TH "PQgeterror" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQgeterror, PQseterror, PQgetErrorField \- libpqtypes error system management functions 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | char *PQgeterror(void); 10 | .br 11 | void PQseterror(const char *\fIformat\fP, ...); 12 | .br 13 | char *PQgetErrorField(int \fIfieldcode\fP); 14 | \fP 15 | .SH DESCRIPTION 16 | .LP 17 | The libpqtypes library maintains a per-thread global error message. The error 18 | message is managed by \fIPQgeterror\fP() and \fiPQseterror\fP(). 19 | 20 | \fiPQseterror\fP() takes a regular printf compatible \fIformat\fP string for 21 | setting the libpqtypes error message. 22 | 23 | \fiPQgetErrorField\fP() gets an error field from the last executed query. 24 | The error field is only set by PQparamExec and PQparamExecPrepared. When 25 | using libpq functions that return results, like PQexec, continue to use 26 | PQresultErrorField. 27 | 28 | To clear the libpqtypes error message, call \fiPQseterror\fP() with a NULL 29 | \fIformat\fP string: PQseterror(NULL); This will also clear error fields. 30 | .SH RETURN VALUE 31 | .LP 32 | \fIPQgeterror\fP() returns the last error message for the calling thread. It will 33 | never return NULL. 34 | .LP 35 | \fIPQgetErrorField\fP() returns the error field, referenced by fieldcode, for 36 | the last executed query. NULL is returned if no value exists. 37 | .SH EXAMPLES 38 | .LP 39 | None. 40 | .SH AUTHOR 41 | .LP 42 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 43 | Written by Andrew Chernow and Merlin Moncure. 44 | .SH REPORTING BUGS 45 | .LP 46 | Report bugs to . 47 | .SH COPYRIGHT 48 | .LP 49 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 50 | .br 51 | This is free software; see the source for copying conditions. 52 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 53 | FOR A PARTICULAR PURPOSE. 54 | .SH SEE ALSO 55 | .LP 56 | \fIpqt-specs(3)\fP 57 | -------------------------------------------------------------------------------- /source/docs/man3/PQgetf.3: -------------------------------------------------------------------------------- 1 | .TH "PQgetf" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQgetf \- Gets one or more values from a PGresult in a scanf fashion. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQgetf(const PGresult *\fIres\fP, int \fItup_num\fP, 10 | .br 11 | const char *\fIformat\fP, ...); 12 | .br 13 | int PQgetvf(const PGresult *\fIres\fP, int \fItup_num\fP, 14 | .br 15 | const char *\fIformat\fP, va_list ap); 16 | \fP 17 | .SH DESCRIPTION 18 | .LP 19 | The \fIPQgetf\fP() and \fIPQgetvf\fP() functions get one or more field 20 | values from a PGresult using a scanf style interface. The \fItup_num\fP 21 | argument indicates which tuple to read values from; values can 22 | only be read from one tuple at a time. 23 | 24 | The \fIformat\fP argument is a data type specifier string 25 | indicating the values to retrieve, like %int4 or #timestamp. Any 26 | number of fields, in any order, can be retrieved in a single call. 27 | Each data type specifier has a cooresponding \fBfield_num, type-args, [...]\fP 28 | contained within the function\'s variable argument list. The 29 | \fBfield_num\fP either indicates tuple field number or tuple field 30 | name, depending on whether the data type in \fIformat\fP used a \'%\' 31 | or \'#\' specifer mark (\`man \fIpqt-specs(3)\fP\'). The \fBtype-args\fP can be one or more 32 | arguments required by the specific data type: for example, "%int4" 33 | would require a pointer to a PGint4. All built-in PostgreSQL data 34 | types require a single pointer value. 35 | .SH RETURN VALUE 36 | .LP 37 | On success, a non-zero value is returned. On error, zero is 38 | returned and \fIPQgeterror(3)\fP will contain an error message. 39 | 40 | If the retrieval of any field fails, the get operation is aborted and 41 | function will fail. Eventhough some fields may have succeeded prior to 42 | the failure, their values should not be trusted. Instead, make 43 | another \fIPQgetf\fP() call. Getting an array or a composite can lead 44 | to memory leaks when getf fails. This is because these types allocate 45 | a PGresult object that must be cleared. To avoid leaks, it is recommended 46 | to perform cleanup even if getf fails, retrieve arrays and composites by 47 | themselves or make them the last field in a getf call. 48 | .SH EXAMPLES 49 | .LP 50 | .SS Using PQgetf on a PGresult 51 | The example uses the libpq PQexec function to execute a query and then uses 52 | \fIPQgetf\fP() to retrieve field values. It is important to note that 53 | libpqtypes execution functions, like \fIPQparamExec(3)\fP, do not need to 54 | generate the PGresult provided to \fIPQgetf\fP(). 55 | .RS 56 | .nf 57 | .LP 58 | \fBint success; 59 | PGint4 i4; 60 | PGtext text; 61 | PGbytea bytea; 62 | PGpoint pt; 63 | PGresult *res = PQexec(conn, "SELECT i,t,b,p FROM tbl"); 64 | 65 | /* Get some field values from the result (order doesn\'t matter) */ 66 | success = PQgetf(res, 67 | 0, /* get field values from tuple 0 */ 68 | "%int4 #text %bytea %point", 69 | /* type format specifiers (get text by name \'#\') */ 70 | 0, &i4, /* get an int4 from field num 0 */ 71 | "t", &text, /* get a text from field name "t" */ 72 | 2, &bytea, /* get a bytea from field num 2 */ 73 | 3, &pt); /* get a point from field num 3 */ 74 | 75 | /* Output an error message using PQgeterror(3). */ 76 | if(!success) 77 | fprintf(stderr, "*ERROR: %s\\n", PQgeterror()); 78 | 79 | /* Output the values, do this before PQclear() */ 80 | else 81 | printf("int4=%d, text=%s, bytea=%d bytes, point=(%f,%f)\\n", 82 | i4, text, bytea.len, pt.x, pt.y); 83 | 84 | PQclear(res); 85 | \fP 86 | .fi 87 | .RE 88 | .SH AUTHOR 89 | .LP 90 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 91 | Written by Andrew Chernow and Merlin Moncure. 92 | .SH REPORTING BUGS 93 | .LP 94 | Report bugs to . 95 | .SH COPYRIGHT 96 | .LP 97 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 98 | .br 99 | This is free software; see the source for copying conditions. 100 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 101 | FOR A PARTICULAR PURPOSE. 102 | .SH SEE ALSO 103 | .LP 104 | \fIpqt-specs(3)\fP, \fIPQgeterror(3)\fP 105 | -------------------------------------------------------------------------------- /source/docs/man3/PQgetvf.3: -------------------------------------------------------------------------------- 1 | .so man3/PQgetf.3 2 | -------------------------------------------------------------------------------- /source/docs/man3/PQinitTypes.3: -------------------------------------------------------------------------------- 1 | .TH PQinitTypes 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQinitTypes \- Initializes libpqtypes with the libpq event system. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQinitTypes(PGconn *conn); 10 | .br 11 | int PQclearTypes(PGconn *\fIconn\fP); 12 | \fP 13 | .SH DESCRIPTION 14 | .LP 15 | libpqtypes makes use of the libpq Event System. Before using 16 | libpqtypes, you must initialize libpqtypes as a libpq EventProc. 17 | The PQinitTypes function takes a PGconn that libpqtypes will be initialized 18 | with; each PGconn requires its own initialization. 19 | 20 | PQclearTypes removes all registered types from the given PGconn. A good use 21 | for this is after a PQresetXXX call when it might be desired to re-register 22 | all types that may have gone stale. 23 | .SH RETURN VALUE 24 | .LP 25 | PQinitTypes and PQclearTypes return zero if it fails and non-zero if it succeeds. 26 | .SH EXAMPLES 27 | .LP 28 | .SS Initializing libpqtypes 29 | The examples shows how to initialize libpqtypes with the libpq event system. 30 | .RS 31 | .nf 32 | .LP 33 | \fB/* call prior to any other libpqtypes functions that operate on conn. */ 34 | PQinitTypes(conn); 35 | \fP 36 | .fi 37 | .RE 38 | .SH AUTHOR 39 | .LP 40 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 41 | Written by Andrew Chernow. 42 | .SH REPORTING BUGS 43 | .LP 44 | Report bugs to . 45 | .SH COPYRIGHT 46 | .LP 47 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 48 | .br 49 | This is free software; see the source for copying conditions. 50 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 51 | FOR A PARTICULAR PURPOSE. 52 | .SH SEE ALSO 53 | .LP 54 | None 55 | 56 | 57 | -------------------------------------------------------------------------------- /source/docs/man3/PQlocalTZInfo.3: -------------------------------------------------------------------------------- 1 | .TH "PQlocalTZInfo" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQlocalTZInfo \- Gets the local machine\'s timezone information. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | void PQlocalTZInfo(time_t *\fIt\fP, int *\fIgmtoff\fP, int *\fIisdst\fP, char **\fItzabbrp\fP); 10 | \fP 11 | .SH DESCRIPTION 12 | .LP 13 | This function retrieves the local machine\'s timezone information. 14 | 15 | If the \fIt\fP argument is not NULL, it represents a time_t value to get timezone 16 | information for. If it is NULL, the current time is used. 17 | 18 | The \fIgmtoff\fP argument will be pointed at the number of seconds from GMT, 19 | same value as the GNU (struct tm).tm_gmtoff extension. 20 | 21 | The \fIisdst\fP argument will be pointed at zero if in standard time, one if in 22 | daylight savings time and negative one if unknown. 23 | 24 | The \fItzabbrp\fP argument will be pointed at the timezone abbreviation, like 25 | PST, ADT, EST, etc.. 26 | .SH RETURN VALUE 27 | .LP 28 | None. 29 | .SH EXAMPLES 30 | .LP 31 | This example gets the local timezone information for a file\'s modified time. 32 | .LP 33 | .RS 34 | .nf 35 | \fBint gmtoff; 36 | int isdst; 37 | char *tzabbr; 38 | struct stat st; 39 | 40 | stat("report.xml", &st); 41 | PQlocalTZInfo(&st.st_mtime, &gmtoff, &isdst, &tzabbr); 42 | \fP 43 | .fi 44 | .RE 45 | .SH RATIONALE 46 | .LP 47 | libpqtypes needs the ability to get the local machine\'s timezone information for the 48 | datetime data types. It later became apparent that a portable way of getting timezone 49 | information was very useful and in demand. Thus, this function was made public. 50 | .SH AUTHOR 51 | .LP 52 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 53 | Written by Andrew Chernow and Merlin Moncure. 54 | .SH REPORTING BUGS 55 | .LP 56 | Report bugs to . 57 | .SH COPYRIGHT 58 | .LP 59 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 60 | .br 61 | This is free software; see the source for copying conditions. 62 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 63 | FOR A PARTICULAR PURPOSE. 64 | .SH SEE ALSO 65 | .LP 66 | \fIpqt-specs(3)\fP 67 | -------------------------------------------------------------------------------- /source/docs/man3/PQparamClear.3: -------------------------------------------------------------------------------- 1 | .so man3/PQparamCreate.3 -------------------------------------------------------------------------------- /source/docs/man3/PQparamCount.3: -------------------------------------------------------------------------------- 1 | .so man3/PQparamCreate.3 -------------------------------------------------------------------------------- /source/docs/man3/PQparamCreate.3: -------------------------------------------------------------------------------- 1 | .TH "PQparamCreate" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQparamCreate, PQparamCount PQparamReset, PQparamClear \- PGparam management functions. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | PGparam *PQparamCreate(const PGconn *\fIconn\fP); 10 | .br 11 | PGparam *PQparamDup(PGparam *\fIparam\fP); 12 | .br 13 | int PQparamCount(PGparam *\fIparam\fP); 14 | .br 15 | void PQparamReset(PGparam *\fIparam\fP); 16 | .br 17 | void PQparamClear(PGparam *\fIparam\fP); 18 | \fP 19 | .SH DESCRIPTION 20 | .LP 21 | These functions manage the opaque PGparam object. 22 | 23 | \fIPQparamCreate\fP() will allocate and initialize a new PGparam object. 24 | After the create call, the PGparam object is ready for use. WARNING: Only 25 | types that have been registered via PQregisterXXX, will be available 26 | to the param. Meaning, the param is not updated with types registered 27 | after the param is created. 28 | 29 | \fIPQparamDup\fP() will duplicate a given PGparam, including any internal 30 | values that have already been put. This is useful in cases where you want 31 | to queue qeuries to execute at a later time, like a connection pooler. The 32 | problem is PGparamCreate requires a connection object, which may not be 33 | available when attempting to enqueue a query with its PGparam object. 34 | Instead, a PGparam object can used for the sole purpose of creating 35 | duplicates while there are no available PGconn objects. 36 | 37 | \fIPQparamCount\fP() gets the number of parameters in a PGparam object. 38 | 39 | \fIPQparamReset\fP() will clear out any previously put parameters, but will 40 | not free any memory. This is useful for application looking to "reuse" a 41 | PGparam object. 42 | 43 | \fIPQparamClear\fP() releases all resources being used by a PGparam object, 44 | the object should not be used after a clear. 45 | 46 | It is very important to call \fIPQparamReset\fP() if you plan on reusing a 47 | PGparam object. 48 | .SH RETURN VALUE 49 | .LP 50 | \fIPQparamCreate\fP() returns a pointer to a PGparam object on success and NULL 51 | if something failed (check \fIPQgeterror(3)\fP for more information). 52 | 53 | \fIPQparamCount\fP() returns the number of parameters in a PGparam object. 54 | 55 | \fIPQparamReset\fP() and \fIPQparamClear\fP() have no return values. If 56 | either function is provided a NULL PGparam pointer, it will silently fail. 57 | .SH EXAMPLES 58 | .LP 59 | None. 60 | .SH AUTHOR 61 | .LP 62 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 63 | Written by Andrew Chernow and Merlin Moncure. 64 | .SH REPORTING BUGS 65 | .LP 66 | Report bugs to . 67 | .SH COPYRIGHT 68 | .LP 69 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 70 | .br 71 | This is free software; see the source for copying conditions. 72 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 73 | FOR A PARTICULAR PURPOSE. 74 | .SH SEE ALSO 75 | .LP 76 | \fIpqt-specs(3)\fP, \fIPQputf(3)\fP, \fIPQgeterror(3)\fP, \fIPQparamExec(3)\fP 77 | -------------------------------------------------------------------------------- /source/docs/man3/PQparamDup.3: -------------------------------------------------------------------------------- 1 | .so man3/PQparamCreate.3 2 | -------------------------------------------------------------------------------- /source/docs/man3/PQparamExec.3: -------------------------------------------------------------------------------- 1 | .TH "PQparamExec" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQparamExec, PQparamExecPrepared \- Executes a paramertized query using the parameters in a PGparam. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | PGresult *PQparamExec(PGconn *\fIconn\fP, PGparam *\fIparam\fP, 10 | .br 11 | const char *\fIcommand\fP, int \fIresultFormat\fP); 12 | .br 13 | PGresult *PQparamExecPrepared(PGconn *\fIconn\fP, PGparam *\fIparam\fP, 14 | .br 15 | const char *\fIstmtName\fP, int \fIresultFormat\fP); 16 | \fP 17 | .SH DESCRIPTION 18 | .LP 19 | The \fIPQparamExec\fP() and \fIPQparamExecPrepared\fP() functions execute 20 | a parameterized query using the parameters in a PGparam. The only difference 21 | between these functions is that \fIPQparamExec\fP() expects a parameterized 22 | \fIcommand\fP string while \fIPQparamExecPrepared\fP() expects a \fIstmtName\fP 23 | previously prepared via PQprepare(). 24 | 25 | Both functions take a \fIparam\fP argument, which must contain the same number 26 | of parameters as either the \fIcommand\fP string or previously prepared \fIstmtName\fP. 27 | Internally, the \fIparam\fP is transformed into parallel arrays that are 28 | supplied to a PQexecParams() or PQexecPrepared() call. 29 | 30 | The \fIresultFormat\fP argument indicates if text or binary results are desired; 31 | a value of zero or one respectively. \fIPQgetf\fP supports both text and binary 32 | result formats, with the exclusion of arrays and composites which only support binary. 33 | .SH RETURN VALUE 34 | .LP 35 | On success, a pointer to a PGresult is returned. On error, NULL is 36 | returned and \fIPQgeterror(3)\fP will contain an error message. 37 | 38 | \fBIMPORTANT!\fP 39 | .br 40 | There is a difference in behavior between \fIPQparamExec\fP() and \fIPQparamExecPrepared\fP() 41 | and the libpq functions they wrap, PQexecParams() and PQexecPrepared(). 42 | \fIPQparamExec\fP() and \fIPQparamExecPrepared\fP() only return a non-NULL 43 | PGresult when the result status is either PGRES_COMMAND_OK, PGRES_TUPLES_OK or 44 | PGRES_EMPTY_QUERY. If these functions detect any other result status, the 45 | PGresult is cleared and a NULL result is returned. Before clearing the PGresult 46 | and returning NULL, these functions first copy the result error message into the 47 | libpqtypes error system, accessible via \fIPQgeterror(3)\fP. This allows applications 48 | to get a result\'s error message without needing the result object. \fIconn\fP error 49 | messages are also copied to the libpqtypes error system. 50 | 51 | This behavior difference provides a single error indicator, a NULL return, and a 52 | single function that can get the error message, \fIPQgeterror\fP(). 53 | .SH EXAMPLES 54 | .LP 55 | .SS Using PQparamExec 56 | The example uses \fIPQparamExec\fP() to execute a query using a PGparam. 57 | The example also demonstrates how to detect a failed exec and output an error 58 | message. 59 | .RS 60 | .nf 61 | .LP 62 | \fBPGparam *param = PQparamCreate(conn); 63 | 64 | if(!PQputf(param, "%text %int4", "ACTIVE", CAT_CAR)) 65 | { 66 | fprintf(stderr, "PQputf: %s\\n", PQgeterror()); 67 | } 68 | else 69 | { 70 | PGresult *res = PQparamExec(conn, param, 71 | "SELECT * FROM t WHERE status=$1 AND category=$2", 1); 72 | 73 | if(!res) 74 | fprintf(stderr, "PQparamExec: %s\\n", PQgeterror()); 75 | else 76 | print_results(res); 77 | 78 | PQclear(res); 79 | } 80 | 81 | PQparamClear(param); 82 | \fP 83 | .fi 84 | .RE 85 | .SS Using PQparamExecPrepared 86 | \fIPQparamExecPrepared\fP() is behaves identically to \fIPQparamExec\fP(), except 87 | \fIPQparamExecPrepared\fP() requires that a statement has been previously prepared 88 | via PQprepare(). Also, a \fIstmtName\fP is supplied rather than a parameterized 89 | \fIcommand\fP string. 90 | .SH AUTHOR 91 | .LP 92 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 93 | Written by Andrew Chernow and Merlin Moncure. 94 | .SH REPORTING BUGS 95 | .LP 96 | Report bugs to . 97 | .SH COPYRIGHT 98 | .LP 99 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 100 | .br 101 | This is free software; see the source for copying conditions. 102 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 103 | FOR A PARTICULAR PURPOSE. 104 | .SH SEE ALSO 105 | .LP 106 | \fIPQparamCreate(3)\fP, \fIPQparamSendQuery(3)\fP, \fIPQparamSendQueryPrepared(3)\fP 107 | -------------------------------------------------------------------------------- /source/docs/man3/PQparamExecPrepared.3: -------------------------------------------------------------------------------- 1 | .so man3/PQparamExec.3 -------------------------------------------------------------------------------- /source/docs/man3/PQparamReset.3: -------------------------------------------------------------------------------- 1 | .so man3/PQparamCreate.3 -------------------------------------------------------------------------------- /source/docs/man3/PQparamSendQuery.3: -------------------------------------------------------------------------------- 1 | .TH "PQparamSendQuery" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQparamSendQuery, PQparamSendQueryPrepared \- Executes an asynchronous paramertized query using the parameters in a PGparam. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQparamSendQuery(PGconn *\fIconn\fP, PGparam *\fIparam\fP, 10 | .br 11 | const char *\fIcommand\fP, int \fIresultFormat\fP); 12 | .br 13 | int PQparamSendQueryPrepared(PGconn *\fIconn\fP, PGparam *\fIparam\fP, 14 | .br 15 | const char *\fIstmtName\fP, int \fIresultFormat\fP); 16 | \fP 17 | .SH DESCRIPTION 18 | .LP 19 | The \fIPQparamSendQuery\fP() and \fIPQparamSendQueryPrepared\fP() functions execute 20 | an asynchronous paramertized query using the parameters in a PGparam. The only difference 21 | between these functions is that \fIPQparamSendQuery\fP() expects a parameterized 22 | \fIcommand\fP string while \fIPQparamSendQueryPrepared\fP() expects a \fIstmtName\fP 23 | previously prepared via PQprepare(). 24 | 25 | Both functions take a \fIparam\fP argument, which must contain the same number 26 | of parameters as either the \fIcommand\fP string or previously prepared \fIstmtName\fP. 27 | Internally, the \fIparam\fP is transformed into parallel arrays that are 28 | supplied to a PQsendQueryParams() or PQsendQueryPrepared() call. 29 | 30 | The \fIresultFormat\fP argument indicates if text or binary results are desired; 31 | a value of zero or one respectively. \fIPQgetf\fP supports both text and binary 32 | result formats, with the exclusion of arrays and composites which only support binary. 33 | 34 | After successfully calling \fIPQparamSendQuery\fP() or \fIPQparamSendQueryPrepared\fP(), 35 | call libpq\'s PQgetResult() one or more times to obtain the results. 36 | .SH RETURN VALUE 37 | .LP 38 | On success, a non-zero value is returned. On error, zero is 39 | returned and \fIPQgeterror(3)\fP will contain an error message. 40 | .SH EXAMPLES 41 | .LP 42 | .SS Using PQparamSendQuery 43 | The example uses \fIPQparamSendQuery\fP() to execute a query using a PGparam. 44 | .RS 45 | .nf 46 | .LP 47 | \fBPGparam *param = PQparamCreate(conn); 48 | 49 | if(!PQputf(param, "%text %int4", "ACTIVE", CAT_CAR)) 50 | { 51 | fprintf(stderr, "PQputf: %s\\n", PQgeterror()); 52 | } 53 | else 54 | { 55 | int success = PQparamSendQuery(conn, param, 56 | "SELECT * FROM t WHERE status=$1 AND category=$2", 1); 57 | 58 | if(!success) 59 | fprintf(stderr, "PQparamSendQuery: %s\\n", PQgeterror()); 60 | else 61 | get_and_print_results(conn); /* calls PQgetResult() */ 62 | } 63 | 64 | PQparamClear(param); 65 | \fP 66 | .fi 67 | .RE 68 | .SS Using PQparamSendQueryPrepared 69 | \fIPQparamSendQueryPrepared\fP() is behaves identically to \fIPQparamSendQuery\fP(), except 70 | \fIPQparamSendQueryPrepared\fP() requires that a statement has been previously prepared 71 | via PQprepare(). Also, a \fIstmtName\fP is supplied rather than a parameterized 72 | \fIcommand\fP string. 73 | .SH AUTHOR 74 | .LP 75 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 76 | Written by Andrew Chernow and Merlin Moncure. 77 | .SH REPORTING BUGS 78 | .LP 79 | Report bugs to . 80 | .SH COPYRIGHT 81 | .LP 82 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 83 | .br 84 | This is free software; see the source for copying conditions. 85 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 86 | FOR A PARTICULAR PURPOSE. 87 | .SH SEE ALSO 88 | .LP 89 | \fIPQparamCreate(3)\fP, \fIPQgeterror(3)\fP, \fIPQparamExec(3)\fP, \fIPQparamExecPrepared(3)\fP 90 | -------------------------------------------------------------------------------- /source/docs/man3/PQparamSendQueryPrepared.3: -------------------------------------------------------------------------------- 1 | .so man3/PQparamSendQuery.3 -------------------------------------------------------------------------------- /source/docs/man3/PQputf.3: -------------------------------------------------------------------------------- 1 | .TH "PQputf" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQputf, PQputvf \- Packs a set of parameters in a PGparam for use with a parameterized query. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQputf(PGparam *\fIparam\fP, const char *\fIformat\fP, ...); 10 | .br 11 | int PQputvf(PGparam *\fIparam\fP, char *\fIstmtBuf\fP, size_t \fIstmtBufLen\fP, 12 | .br 13 | const char *\fIformat\fP, va_list \fIap\fP); 14 | \fP 15 | .SH DESCRIPTION 16 | .LP 17 | The \fIPQputf\fP() and \fIPQputvf\fP() functions put one or more query 18 | parameters into a PGparam using a printf style interface. Any number 19 | of parameters can be put at the same time. 20 | 21 | The \fIformat\fP argument is a data type specifier string indicating the 22 | parameters being put, such as "%int4 %polygon". \fIformat\fP cannot be 23 | NULL or an empty string. The variable argument list must match the 24 | \fIformat\fP, either "..." or \fIap\fP. The number of arguments required 25 | for each data type is dependant on the data type itself; built-in 26 | PostgreSQL types always require a single argument. 27 | 28 | The \fIPQputvf\fP() function can construct a parameterized command string 29 | from \fIformat\fP, as long as \fIstmtBuf\fP and \fIstmtBufLen\fP have been 30 | provided. If the construction of \fIstmtBuf\fP is not desired, set it 31 | to NULL and set \fIstmtBufLen\fP to 0. When a constructed statement is desired, 32 | the contents of \fIformat\fP will be copied to \fIstmtBuf\fP and 33 | all data type specifiers, like "%int4", will be replaced with $1, $2, etc... 34 | syntax. The result is a parameterized statement in synch with \fIparam\fP 35 | and ready to be executed. For instance: if \fIspec\fP is "SELECT %int4 + %int4", 36 | the resulting \fIstmtBuf\fP would be "SELECT $1 + $2". 37 | .SH RETURN VALUE 38 | .LP 39 | On success, a non-zero value is returned. On error, zero is 40 | returned and \fIPQgeterror(3)\fP will contain an error message. 41 | 42 | If any put operation fails, the \fIparam\fP is reverted back to the number 43 | of parameters it had prior to the function call; partial puts are not allowed. 44 | .SH EXAMPLES 45 | .LP 46 | .SS Using PQputf 47 | The example uses \fIPQputf\fP() to put a few parameters into a PGparam. Note 48 | that the last parameter puts an SQL NULL as an int4 using the pass-by-pointer 49 | type-specifier pointer flag '*', rather than pass-by-value. 50 | .RS 51 | .nf 52 | .LP 53 | \fBPGtext text = "foobar"; 54 | PGint8 i8 = PQT_INT64CONST(1099511627776); 55 | PGint4 *i4ptr = NULL; 56 | PGparam *param = PQparamCreate(conn); 57 | 58 | if(!PQputf(param, "%text %int8 %int4*", text, i8, i4ptr)) 59 | fprintf(stderr, "*ERROR: %s\\n", PQgeterror()); 60 | 61 | PQparamClear(param); 62 | \fP 63 | .fi 64 | .RE 65 | .SS Using PQputvf 66 | The example creates an application function named execf. execf is a 67 | wrapper to \fIPQputvf\fP(), as well as \fIPQparamExec(3)\fP. It is 68 | similar to \fIPQexecf\fP(). The only difference is that the PQexecf 69 | implementation uses a smaller stack buffer and allocates heap memory 70 | when needed. 71 | .RS 72 | .nf 73 | .LP 74 | \fBPGresult * 75 | execf(PGconn *conn, const char *format, ...) 76 | { 77 | va_list ap; 78 | char stmt[32768]; 79 | PGparam *param; 80 | PGresult *res = NULL; 81 | 82 | /* create the temporary PGparam */ 83 | if(!(param = PQparamCreate(conn))) 84 | return NULL; /* PQseterror already called */ 85 | 86 | /* put the params, create the stmt and exec it */ 87 | va_start(ap, format); 88 | if(PQputvf(param, stmt, sizeof(stmt), format, ap)) 89 | res = PQparamExec(conn, param, stmt, 1); // resfmt is binary 90 | va_end(ap); 91 | 92 | /* param must be cleared */ 93 | PQparamClear(param); 94 | return res; 95 | } 96 | 97 | /* Example: execf will put 2 ints and execute "SELECT $1 + $2" */ 98 | PGresult *res = execf(conn, "SELECT %int4 + %int4", 100, 67); 99 | if(!res) 100 | fprintf(stderr, "*ERROR: %s\\n", PQgeterror()); 101 | \fP 102 | .fi 103 | .RE 104 | .SH AUTHOR 105 | .LP 106 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 107 | Written by Andrew Chernow and Merlin Moncure. 108 | .SH REPORTING BUGS 109 | .LP 110 | Report bugs to . 111 | .SH COPYRIGHT 112 | .LP 113 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 114 | .br 115 | This is free software; see the source for copying conditions. 116 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 117 | FOR A PARTICULAR PURPOSE. 118 | .SH SEE ALSO 119 | .LP 120 | \fIpqt-specs(3)\fP, \fIPQparamCreate(3)\fP, \fIPQgeterror(3)\fP, \fIPQseterror(3)\fP 121 | -------------------------------------------------------------------------------- /source/docs/man3/PQputvf.3: -------------------------------------------------------------------------------- 1 | .so man3/PQputf.3 2 | -------------------------------------------------------------------------------- /source/docs/man3/PQregisterComposites.3: -------------------------------------------------------------------------------- 1 | .TH "PQregisterComposites" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQregisterComposites \- Registers a composites. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | int PQregisterComposites(PGconn *\fIconn\fP, PGregisterType *\fItypes\fP, 9 | .br 10 | int \fIcount\fP); 11 | \fP 12 | .SH DEPRECATED 13 | .LP 14 | THIS FUNCTION IS DEPRECATED. New applications should use PQregisterTypes. 15 | This function is now a wrapper to PQregisterTypes. 16 | .SH DESCRIPTION 17 | .LP 18 | The \fIPQregisterComposites\fP() function allows an application 19 | to register one or more composites. This function can be 20 | called as many times as an application needs on a PGconn. 21 | 22 | This function must execute a query against the backend to retrieve type 23 | information for each composite, thus this should not be called from within 24 | a transaction. It is recommended to register multiple composites at 25 | the same time to avoid round trip overhead. 26 | 27 | The \fItypes\fP argument is an array containing \fIcount\fP composite types 28 | to register. Composites do not use the typput or typget members of the 29 | PGregisterType structure, thus these memebrs are ignored. If any composite 30 | does not exist, the register is aborted. 31 | 32 | NOTE: The typname member of the PGregisterType structure can optionally 33 | contain the type's schema: schema.typname. 34 | 35 | WARNING: \fIPQparamCreate\fP is only aware of types that have already been 36 | registered. If you need to put a composite into a param, make sure it is first 37 | registered. 38 | 39 | .SH EXAMPLES 40 | .LP 41 | .SS Using PQregisterComposites 42 | The example registers two composite types. 43 | .RS 44 | .nf 45 | .LP 46 | \fBPGregisterType comp_types[] = { 47 | {"myschema.simple", NULL, NULL}, 48 | {"complex", NULL, NULL} 49 | }; 50 | 51 | if (!PQregisterComposites(conn, comp_types, 2)) 52 | fprintf(stderr, "PQregisterComposites: %s\\n", PQgeterror()); 53 | \fP 54 | .fi 55 | .RE 56 | .SH RETURN VALUE 57 | .LP 58 | On success, a non-zero value is returned. On error, zero is 59 | returned and \fIPQgeterror(3)\fP will contain an error message. 60 | .SH EXAMPLES 61 | .LP 62 | None. 63 | .SH AUTHOR 64 | .LP 65 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 66 | Written by Andrew Chernow and Merlin Moncure. 67 | .SH REPORTING BUGS 68 | .LP 69 | Report bugs to . 70 | .SH COPYRIGHT 71 | .LP 72 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 73 | .br 74 | This is free software; see the source for copying conditions. 75 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 76 | FOR A PARTICULAR PURPOSE. 77 | .SH SEE ALSO 78 | .LP 79 | \fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP 80 | 81 | -------------------------------------------------------------------------------- /source/docs/man3/PQregisterResult.3: -------------------------------------------------------------------------------- 1 | .TH "PQregisterResult" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQregisterResult \- Registers sub-classes, composites and user-defined types found within a PGresult. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | int PQregisterResult(PGconn *\fIconn\fP, int \fIwhich\fP, PGregisterType *\fItypes\fP, 9 | .br 10 | int \fIcount\fP, PGresult *\fIres\fP); 11 | \fP 12 | .SH DESCRIPTION 13 | .LP 14 | The \fIPQregisterResult\fP() function registers the types found within 15 | a given PGresult, thus this function makes no calls to a PostgreSQL server 16 | since the result data is already available. 17 | 18 | The \fIwhich\fP argument can be PQT_COMPOSITE or PQT_USERDEFINED, 19 | but not PQT_SUBCLASS. The only reason being sub-classes don't talk to 20 | the server so they have no result set. 21 | 22 | The \fItypes\fP argument is an array containing \fIcount\fP types 23 | to register. This array must be identical to what was provided to 24 | the originating PQregisterTypes call. 25 | 26 | The \fIres\fP argument is a PGresult normally created by calling 27 | PQregisterTypes followed by PQgetResult. However, it is possible to 28 | create your own result via PQmakeEmptyPGresult, PQsetResultAttrs, 29 | PQsetvalue and call this function. This approach is a bit risky being 30 | how the result set generated by type lookup queries are internal and 31 | subject to change. 32 | 33 | .SH EXAMPLES 34 | .LP 35 | .SS Using PQregisterResult 36 | The example registers two composite types asynchronously. It is worth 37 | noting that the PGresult obtained via PQgetResult can be cached by an 38 | application and used when creating new connections, as a way to avoid 39 | repeatedly performing type lookups with the server. 40 | .RS 41 | .nf 42 | .LP 43 | \fBPGregisterType comp_types[] = { 44 | {"myschema.simple", NULL, NULL}, 45 | {"complex", NULL, NULL} 46 | }; 47 | 48 | /* asynchronous registration */ 49 | if (PQregisterTypes(conn, PQT_COMPOSITE, comp_types, 2, 1)) 50 | { 51 | /* example of a typical event loop */ 52 | for(;;) 53 | { 54 | int n; 55 | fd_set set; 56 | int fd = PQsocket(conn); 57 | struct timeval tv = {0, 500000}; 58 | 59 | FD_ZERO(&set); 60 | FD_SET(fd, &set); 61 | n = select(fd + 1, &set, NULL, NULL, &tv); //or kqueue,epoll,poll,etc.. 62 | 63 | if (n == -1) 64 | { 65 | //error 66 | } 67 | else if (n == 0) 68 | { 69 | //timeout, do other work .... 70 | } 71 | else 72 | { 73 | PGresult *res; 74 | 75 | PQconsumeInput(conn); 76 | if(!PQisBusy(conn)) 77 | { 78 | /* done */ 79 | if(!(res = PQgetResult(conn))) 80 | break; 81 | 82 | n = PQregisterResult(conn, PQT_COMPOSITE, comp_types, 2, res); 83 | 84 | /* This could also be cached and reused with PQregisterResult */ 85 | PQclear(res); 86 | 87 | if (!n) 88 | //error, consult PQgeterror() 89 | } 90 | } 91 | } 92 | } 93 | \fP 94 | .fi 95 | .RE 96 | .SH RETURN VALUE 97 | .LP 98 | On success, a non-zero value is returned. On error, zero is 99 | returned and \fIPQgeterror(3)\fP will contain an error message. 100 | .SH EXAMPLES 101 | .LP 102 | None. 103 | .SH AUTHOR 104 | .LP 105 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 106 | Written by Andrew Chernow. 107 | .SH REPORTING BUGS 108 | .LP 109 | Report bugs to . 110 | .SH COPYRIGHT 111 | .LP 112 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 113 | .br 114 | This is free software; see the source for copying conditions. 115 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 116 | FOR A PARTICULAR PURPOSE. 117 | .SH SEE ALSO 118 | .LP 119 | \fIPQregisterTypes(3)\fP, \fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /source/docs/man3/PQregisterSubClasses.3: -------------------------------------------------------------------------------- 1 | .TH "PQregisterSubClasses" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQregisterSubClasses \- Registers a type aliases or sub-classes. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQregisterSubClasses(PGconn *\fIconn\fP, PGregisterType *\fItypes\fP, 10 | .br 11 | int \fIcount\fP); 12 | \fP 13 | .SH DEPRECATED 14 | .LP 15 | THIS FUNCTION IS DEPRECATED. New applications should use PQregisterTypes. 16 | This function is now a wrapper to PQregisterTypes. 17 | .SH DESCRIPTION 18 | .LP 19 | The \fIPQregisterSubClasses\fP() function allows an application 20 | to register an alias or sub-class of another type. 21 | 22 | The \fItypes\fP argument is an array containing \fIcount\fP sub class types 23 | to register. The typname member of the PGregisterType structure must 24 | specify an inheritence relationship: ex. "myint=int4" where myint inherits 25 | from int4. The \'=\' is called the inheritence operator. If both typput 26 | and typget members of the PGregisterType structure are NULL, the type at 27 | that element will behave identically to the type it is inheriting 28 | from; an alias. Otherwise, the base type's put and/or get routines will 29 | be overridden. 30 | 31 | NOTE: The typname member of the PGregisterType structure can optionally 32 | contain the type's schema: schema.typname. 33 | 34 | WARNING: \fIPQparamCreate\fP is only aware of types that have already been 35 | registered. If you need to put a type into a param, make sure it is first 36 | registered. 37 | 38 | .SH RETURN VALUE 39 | .LP 40 | On success, a non-zero value is returned. On error, zero is 41 | returned and \fIPQgeterror(3)\fP will contain an error message. 42 | .SH EXAMPLES 43 | .LP 44 | None. 45 | .SH AUTHOR 46 | .LP 47 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 48 | Written by Andrew Chernow and Merlin Moncure. 49 | .SH REPORTING BUGS 50 | .LP 51 | Report bugs to . 52 | .SH COPYRIGHT 53 | .LP 54 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 55 | .br 56 | This is free software; see the source for copying conditions. 57 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 58 | FOR A PARTICULAR PURPOSE. 59 | .SH SEE ALSO 60 | .LP 61 | \fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP 62 | 63 | -------------------------------------------------------------------------------- /source/docs/man3/PQregisterTypes.3: -------------------------------------------------------------------------------- 1 | .TH "PQregisterTypes" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQregisterTypes \- Registers sub-classes, composites and user-defined types. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | int PQregisterTypes(PGconn *\fIconn\fP, int \fIwhich\fP, PGregisterType *\fItypes\fP, 9 | .br 10 | int \fIcount\fP, int \fIasync\fP); 11 | \fP 12 | .SH DESCRIPTION 13 | .LP 14 | The \fIPQregisterTypes\fP() function allows an application 15 | to register one or more PQT_SUBCLASS, PQT_COMPOSITE or PQT_USERDEFINED 16 | types which is defined by the \fIwhich\fP argument. 17 | 18 | When \fIwhich\fP is PQT_COMPOSITE or PQT_USERDEFINED, this function must 19 | execute a query against the backend to retrieve type information, thus 20 | this should not be called from within a transaction. It is recommended to 21 | register multiple composites at the same time to avoid round trip overhead. 22 | 23 | The \fItypes\fP argument is an array containing \fIcount\fP types 24 | to register. Composites do not use the typput or typget members of the 25 | PGregisterType structure, thus these memebrs are ignored. If any composite 26 | does not exist, the register is aborted. User-defined types must set either 27 | typput and/or typget for each type. Sub-classes typname member of the 28 | PGregisterType structure must specify an inheritence relationship: 29 | ex. "myint=int4" where myint inherits from int4. The \'=\' is called the 30 | inheritence operator. If both typput and typget members of the PGregisterType 31 | structure are NULL, the type at that element will behave identically to the 32 | type it is inheriting from; an alias. Otherwise, the base type's put and/or 33 | get routines will be overridden. 34 | 35 | The \fIasync\fP argument indicates if the asynchronous type registration 36 | should be used. When non-zero, the caller must proceed in the normal 37 | async libpq fashion (PQconsumeInput, PQisBusy, PQgetResult) and provide a 38 | PGresult to PQregisterResult when obtained to complete the registration. 39 | 40 | NOTE: The typname member of the PGregisterType structure can optionally 41 | contain the type's schema: schema.typname. 42 | 43 | WARNING: \fIPQparamCreate\fP is only aware of types that have already been 44 | registered. If you need to put a type into a param, make sure it is first 45 | registered. 46 | 47 | .SH EXAMPLES 48 | .LP 49 | .SS Using PQregisterTypes 50 | The example registers two composite types. 51 | .RS 52 | .nf 53 | .LP 54 | \fBPGregisterType comp_types[] = { 55 | {"myschema.simple", NULL, NULL}, 56 | {"complex", NULL, NULL} 57 | }; 58 | 59 | if (!PQregisterTypes(conn, PQT_COMPOSITE, comp_types, 2, 0)) 60 | fprintf(stderr, "PQregisterTypes: %s\\n", PQgeterror()); 61 | \fP 62 | .fi 63 | .RE 64 | .SH RETURN VALUE 65 | .LP 66 | On success, both PQregisterTypes returns a non-zero value. 67 | On error, zero is returned and \fIPQgeterror(3)\fP will contain an 68 | error message. 69 | .SH EXAMPLES 70 | .LP 71 | None. 72 | .SH AUTHOR 73 | .LP 74 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 75 | Written by Andrew Chernow. 76 | .SH REPORTING BUGS 77 | .LP 78 | Report bugs to . 79 | .SH COPYRIGHT 80 | .LP 81 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 82 | .br 83 | This is free software; see the source for copying conditions. 84 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 85 | FOR A PARTICULAR PURPOSE. 86 | .SH SEE ALSO 87 | .LP 88 | \fIPQregisterResult(3)\fP, \fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP 89 | 90 | 91 | -------------------------------------------------------------------------------- /source/docs/man3/PQregisterUserDefinedTypes.3: -------------------------------------------------------------------------------- 1 | .TH "PQregisterUserDefinedTypes" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQregisterUserDefinedTypes \- Registers a user-defined types. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQregisterUserDefinedTypes(PGconn *\fIconn\fP, PGregisterType *\fItypes\fP, 10 | .br 11 | int \fIcount\fP); 12 | \fP 13 | .SH DEPRECATED 14 | .LP 15 | THIS FUNCTION IS DEPRECATED. New applications should use PQregisterTypes. 16 | This function is now a wrapper to PQregisterTypes. 17 | .SH DESCRIPTION 18 | .LP 19 | The \fIPQregisterUserDefinedTypes\fP() function allows an application 20 | to register one or more user-defined types at runtime. User-defined types 21 | are custom types in a backend that implement their own C procedures for 22 | in/out/send/recv. 23 | 24 | This function must execute a query against the backend to retrieve type 25 | information for each user-defined type, thus this should not be called 26 | from within a transaction. It is recommended to register multiple types at 27 | the same time to avoid round trip overhead. 28 | 29 | The \fItypes\fP argument is an array containing \fIcount\fP user-defined 30 | types to register. If any type does not exist, the register 31 | is aborted. Either typput and/or typget must be specified for each type 32 | in the \fItypes\fP array. 33 | 34 | NOTE: The typname member of the PGregisterType structure can optionally 35 | contain the type's schema: schema.typname. 36 | 37 | WARNING: \fIPQparamCreate\fP is only aware of types that have already been 38 | registered. If you need to put a type into a param, make sure it is first 39 | registered. 40 | 41 | \fBUser-defined Types Registration\fP 42 | .br 43 | This example registers two user-defined types. 44 | .nf 45 | .RS 46 | .LP 47 | \fBPGregisterType types[] = { 48 | {"graphics.rgb", rgb_put, rgb_get}, 49 | {"graphics.digon", digon_put, digon_get} 50 | }; 51 | 52 | if (!PQregisterUserDefinedTypes(conn, types, 2)) 53 | fprintf(stderr, "PQregisterUserDefinedTypes: %s\\n", PQgeterror()); 54 | \fP 55 | .RE 56 | .fi 57 | 58 | .SH RETURN VALUE 59 | .LP 60 | On success, a non-zero value is returned. On error, zero is 61 | returned and \fIPQgeterror(3)\fP will contain an error message. 62 | .SH EXAMPLES 63 | .LP 64 | None. 65 | .SH AUTHOR 66 | .LP 67 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 68 | Written by Andrew Chernow and Merlin Moncure. 69 | .SH REPORTING BUGS 70 | .LP 71 | Report bugs to . 72 | .SH COPYRIGHT 73 | .LP 74 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 75 | .br 76 | This is free software; see the source for copying conditions. 77 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 78 | FOR A PARTICULAR PURPOSE. 79 | .SH SEE ALSO 80 | .LP 81 | \fIpqt-handlers(3)\fP, \fIPQputf(3)\fP, \fIPQgetf(3)\fP 82 | 83 | -------------------------------------------------------------------------------- /source/docs/man3/PQsendf.3: -------------------------------------------------------------------------------- 1 | .so man3/PQexecf.3 -------------------------------------------------------------------------------- /source/docs/man3/PQsendvf.3: -------------------------------------------------------------------------------- 1 | .so man3/PQexecf.3 -------------------------------------------------------------------------------- /source/docs/man3/PQseterror.3: -------------------------------------------------------------------------------- 1 | .so man3/PQgeterror.3 -------------------------------------------------------------------------------- /source/docs/man3/PQspecPrepare.3: -------------------------------------------------------------------------------- 1 | .TH "PQspecPrepare" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQspecPrepare \- Prepares a libpqtypes specifier format string. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQspecPrepare(PGconn *\fIconn\fP, const char *\fIname\fP, 10 | .br 11 | const char *\fIformat\fP, int \fIis_stmt\fP); 12 | .br 13 | void PQclearSpecs(PGconn *\fIconn\fP); 14 | \fP 15 | .SH DESCRIPTION 16 | .LP 17 | PQspecPrepare allows an application to prepare specifier format strings 18 | that will be used frequently. By preparing a specifier format string, 19 | one avoids the parsing and type handler lookup costs. This becomes a 20 | significant win when managing large result sets or arrays, where the 21 | specifier format, like "%int4 %text %bytea", must be prepared for each 22 | tuple/element. 23 | 24 | As with PQregisterXXX, only specifier format strings prepared prior 25 | to the creation of a PGresult or PGparam, will be available for use. This is 26 | because the prepared type spec is cached within a PGconn object and copied 27 | to all subsequent PGparam and PGresult objects. 28 | 29 | Every prepared type spec is given a \fIname\fP, which is used as its unique identifier. 30 | To use a prepared type spec, the \fIname\fP is provided where ever a regular 31 | specifier format string is allowed, like PQputf or PQgetf. The \fIname\fP must 32 | be proceeded by a \'@\' AT sign. For more information about the syntax, 33 | see the \fIpqt-specs(3)\fP man page. 34 | 35 | The \fIformat\fP argument is the specifier format string being prepared. When 36 | this is NULL, the \fIname\fP prepared type spec is removed from the PGconn\'s 37 | internal array. 38 | 39 | The \fIis_stmt\fP argument indicates if a parementerized statement version of 40 | \fIformat\fP should be cached along with the prepared type spec. This means 41 | all type specifiers in \fIformat\fP, like "%int4", will be converted to "$1" 42 | syntax. When is_stmt is non-zero, a statement will created and cached. 43 | For more information on specifer format string to paremterized statements, see 44 | the \fIPQputf(3)\fP man page. NOTE: to use a prepared type spec with 45 | execution functions like PQexecf, \fIis_stmt\fP must be set to non-zero. 46 | 47 | PQclearSpecs removes all prepared specifiers from the given PGconn, as 48 | opposed to removing them one by one by setting PQspecPrepare's format 49 | argument to NULL. A good use for this is after a PQresetXXX call when it 50 | might be desired to re-prepare all type specifiers. 51 | 52 | Functions that support the use of a prepared type spec are: \fIPQputf\fP, 53 | \fIPQputvf\fP, \fIPQgetf\fP, \fIPQgetvf\fP, \fIPQexecf\fP, \fIPQexecvf\fP, 54 | \fIPQsendf\fP, \fIPQsendvf\fP, \fIPQparamExec\fP, \fIPQparamSendQuery\fP. 55 | 56 | \fBHINT:\fP A good rule of thumb for using prepared type specs, is when there 57 | are a large number of PQputf/PQgetf calls per statement execution. This 58 | commonly occurs when dealing with large result sets and arrays. 59 | .SH RETURN VALUE 60 | .LP 61 | PQspecPrepare and PQclearSpecs return a nonzero value on success and 62 | zero if it fails. Use PQgeterror() to get an error message. 63 | .SH EXAMPLES 64 | .LP 65 | This example prepares a type spec and issues some PQputf calls. 66 | .LP 67 | .RS 68 | .nf 69 | \fBint i; 70 | PQparam *param; 71 | 72 | if(!PQspecPrepare(conn, "prepared_spec", "%int4 %text", 0)) 73 | { 74 | fprintf(stderr, "PQspecPrepare: %s\n", PQgeterror()); 75 | exit(1); 76 | } 77 | 78 | /* create after preparing spec */ 79 | param = PQparamCreate(conn); 80 | 81 | for(i=0; i < 100000; i++) 82 | { 83 | /* NOTE: nothing else can be in format string */ 84 | PQputf(param, "@prepared_spec", 4, "text"); 85 | } 86 | 87 | /* This elects to prepare a statement as well. After this returns, 88 | * "SELECT myfunc($1, $2)" will be cached along with the prepared spec. 89 | */ 90 | PQspecPrepare(conn, "myfunc", "SELECT myfunc(%int4, %text)", 1); 91 | 92 | /* "myfunc" tells execf to execute "SELECT myfunc($1, $2)". If is_stmt 93 | * was set to zero during the PQspecPrepare, the below would be invalid 94 | * because execf doesn't know what to execute. 95 | */ 96 | PQexecf(conn, "@myfunc", 123, "text"); 97 | 98 | /* clear'm all */ 99 | PQclearSpecs(conn); 100 | \fP 101 | .fi 102 | .RE 103 | .SH RATIONALE 104 | .LP 105 | None. 106 | .SH AUTHOR 107 | .LP 108 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 109 | Written by Andrew Chernow. 110 | .SH REPORTING BUGS 111 | .LP 112 | Report bugs to . 113 | .SH COPYRIGHT 114 | .LP 115 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 116 | .br 117 | This is free software; see the source for copying conditions. 118 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 119 | FOR A PARTICULAR PURPOSE. 120 | .SH SEE ALSO 121 | .LP 122 | \fIpqt-specs(3)\fP, \fIPQgetf(3)\fP, \fIPQputf(3)\fP. 123 | 124 | -------------------------------------------------------------------------------- /source/docs/man3/PQtypesRegister.3: -------------------------------------------------------------------------------- 1 | .TH PQtypesRegister 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | PQtypesRegister \- Registers libpqtypes with the libpq event system. 4 | .SH SYNOPSIS 5 | .LP 6 | \fB#include 7 | .br 8 | .sp 9 | int PQtypesRegister(PGconn *conn); 10 | \fP 11 | .SH DEPRECATED 12 | .LP 13 | THIS FUNCTION IS DEPRECATED. New applications should use PQinitTypes. 14 | This function is now a wrapper to PQinitTypes. 15 | .SH DESCRIPTION 16 | .LP 17 | libpqtypes makes use of the libpq Event System. Before using 18 | libpqtypes, you must register libpqtypes as a libpq EventProc. 19 | The function takes a PGconn that libpqtypes will be registered 20 | with; each PGconn requires its own registration. 21 | .SH RETURN VALUE 22 | .LP 23 | The function returns zero if it fails and non-zero if it succeeds. 24 | .SH EXAMPLES 25 | .LP 26 | .SS Registering libpqtypes 27 | The examples shows how to register libpqtypes with the libpq event system. 28 | .RS 29 | .nf 30 | .LP 31 | \fB/* call prior to any other libpqtypes functions that operate on conn. */ 32 | PQtypesRegister(conn); 33 | \fP 34 | .fi 35 | .RE 36 | .SH AUTHOR 37 | .LP 38 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 39 | Written by Andrew Chernow and Merlin Moncure. 40 | .SH REPORTING BUGS 41 | .LP 42 | Report bugs to . 43 | .SH COPYRIGHT 44 | .LP 45 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 46 | .br 47 | This is free software; see the source for copying conditions. 48 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 49 | FOR A PARTICULAR PURPOSE. 50 | .SH SEE ALSO 51 | .LP 52 | None 53 | 54 | -------------------------------------------------------------------------------- /source/docs/man3/pqt-composites.3: -------------------------------------------------------------------------------- 1 | .TH "pqt-composites" 3 2008-2015 "libpqtypes" "libpqtypes Manual" 2 | .SH NAME 3 | pqt-composites \- A manual for libpqtypes composite handling. 4 | .SH NOTE TO READER 5 | .LP 6 | Please read the \fIpqt-specs(3)\fP manual page prior to this document. 7 | This document does not explain how to put or get data types. It 8 | only describes how to put or get composites and composite arrays. 9 | .SH DESCRIPTION 10 | .LP 11 | A composite is put using the PGparam structure. Each attribute of a 12 | composite is put into a PGparam. When all attributes have been put, 13 | the PGparam is put into another PGparam. Composites must be registered 14 | on a per connection basis, \`man \fIpqt-handlers(3)\fP\'. 15 | 16 | To get a composite, a PGresult structure is used. Each composite 17 | attribute is a field of the result. For non-array composites, there 18 | is always only one tuple. 19 | 20 | Composites are only handled using binary format. This means that any type used 21 | as a composite attribute must be put and gotten in binary format. If a 22 | user-defined type does not implement a send and recv function in the backend, 23 | it can not be used as a composite attribute. 24 | .SS Simple Composite Example 25 | .LP 26 | This example demostrates the basics of putting and getting a composite type. 27 | .nf 28 | .RS 29 | \fB 30 | CREATE TYPE simple AS (a int4, t text); 31 | 32 | PGregisterType type = {"simple", NULL, NULL}; 33 | 34 | /* need to register the simple composite */ 35 | PQregisterTypes(conn, PQT_COMPOSITE, &type, 1, 0); 36 | 37 | /* Composite attributes are put into PGparam structures */ 38 | PGparam *simple = PQparamCreate(conn); 39 | 40 | /* put the simple composite attributes */ 41 | PQputf(simple, "%int4 %text*", 45, "foobar"); 42 | 43 | /* Put an int4 and a simple composite */ 44 | PGparam *param = PQparamCreate(conn); 45 | PQputf(param, "%int4 %simple", 10, simple); 46 | PQparamClear(simple); 47 | 48 | /* exec an insert */ 49 | res = PQparamExec(conn, param, "INSERT INTO t VALUES($1,$2)", resfmt); 50 | PQparamClear(param); 51 | 52 | /* ------------------------- 53 | * Getting a composite 54 | */ 55 | 56 | PGint4 i4; 57 | PGtext textp; 58 | char text[80]; 59 | PGresult *simple; 60 | 61 | /* Get a simple composite, provide a ptr to a PGresult ptr. */ 62 | PQgetf(result, 0, "%simple", 0, &simple); 63 | 64 | /* no longer needed */ 65 | PQclear(result); 66 | 67 | /* Get the simple composite attributes from the simple result. 68 | * Reference fields by name by using a '#' rather than a '%'. 69 | * The field names are the composite attributes. 70 | */ 71 | PQgetf(simple, 0, "#int4 #text", "a", &i4, "t", &textp); 72 | strcpy(text, textp); 73 | PQclear(simple); 74 | \fP 75 | .RE 76 | .nf 77 | 78 | In the above example, we used the \'#\' specifier mark to reference 79 | fields by their name. The field names for a composite result object 80 | are the composite attribute names. 81 | .SS Nested Composite example: 82 | .LP 83 | The below example puts and gets a nested composite. The simple composite 84 | is used as an attribute within the complex composite. 85 | .nf 86 | .RS 87 | \fB 88 | CREATE TYPE simple AS (a int4, t text) 89 | CREATE TYPE complex AS (f8 float8, s simple); 90 | 91 | /* need to register simple and complex */ 92 | PGregisterType types[] = { 93 | {"simple", NULL, NULL}, 94 | {"complex", NULL, NULL} 95 | }; 96 | 97 | PQregisterTypes(conn, PQT_COMPOSITE, types, 2, 0); 98 | 99 | /* Composite attributes are put into PGparam structures */ 100 | PGparam *simple = PQparamCreate(conn); 101 | PGparam *complex = PQparamCreate(conn); 102 | 103 | /* put the simple composite attributes */ 104 | PQputf(simple, "%int4 %text*", 45, "foobar"); 105 | 106 | /* put the complex composite attributes, which includes 107 | * a nested composite. 108 | */ 109 | PQputf(complex, "%float8 %simple", 111.2223334, simple); 110 | 111 | /* no longer needed */ 112 | PQparamClear(simple); 113 | 114 | /* Put an int4 and a complex composite */ 115 | PGparam *param = PQparamCreate(conn); 116 | PQputf(param, "%int4 %complex", 10, complex); 117 | PQparamClear(complex); 118 | 119 | /* exec an insert */ 120 | res = PQparamExec(conn, param, "INSERT INTO t VALUES($1,$2)", resfmt); 121 | PQparamClear(param); 122 | 123 | /* ------------------------- 124 | * Getting a nested composite 125 | */ 126 | 127 | PGfloat8 f8; 128 | PGint4 i4; 129 | PGtext textp; 130 | char text[80]; 131 | PGresult *complex; 132 | PGresult *simple; 133 | 134 | /* Get the complex composite, provide a ptr to a PGresult ptr. */ 135 | PQgetf(result, 0, "%complex", 0, &complex); 136 | 137 | /* no longer needed */ 138 | PQclear(result); 139 | 140 | /* Get the complex composite attributes from the complex result. 141 | * Composite attributes are the result fields. When getting 142 | * a single composite, non-array, only tuple 0 will exist. 143 | * For the nested simple composite, we again provide a ptr to 144 | * a PGresult ptr. 145 | */ 146 | PQgetf(complex, 0, "%float8 %simple", 0, &f8, 1, &simple); 147 | 148 | /* no longer needed */ 149 | PQclear(complex); 150 | 151 | /* Get the simple composite attributes from the simple result. 152 | * Reference fields by name by using a '#' rather than a '%'. 153 | */ 154 | PQgetf(simple, 0, "#int4 #text", "a", &i4, "t", &textp); 155 | strcpy(text, textp); 156 | PQclear(simple); 157 | \fP 158 | .RE 159 | .nf 160 | .SS An array of composites: 161 | .LP 162 | This example makes an array of complex composites. It builds 163 | off the previous example. 164 | .nf 165 | .RS 166 | \fB 167 | int i; 168 | PGarray complex_arr; 169 | PGparam *simple = PQparamCreate(conn); 170 | PGparam *complex = PQparamCreate(conn); 171 | 172 | complex_arr.ndims = 0; 173 | complex_arr.param = PQparamCreate(conn); 174 | 175 | for(i=0; i < 100; i++) 176 | { 177 | /* put the simple composite attributes */ 178 | PQputf(simple, "%int4 %text*", 45, "foobar"); 179 | 180 | /* put the complex composite attributes, which includes 181 | * a nested composite. 182 | */ 183 | PQputf(complex, "%float8 %simple", 111.2223334, simple); 184 | 185 | /* put the complex composite */ 186 | PQputf(complex_arr.param, "%complex", complex); 187 | 188 | /* You must reset the simple and complex composites for 189 | * the next loop iteration. 190 | */ 191 | PQparamReset(simple); 192 | PQparamReset(complex); 193 | } 194 | 195 | /* not needed anymore */ 196 | PQparamClear(simple); 197 | PQparamClear(complex); 198 | 199 | /* Put a complex composite array */ 200 | PGparam *param = PQparamCreate(conn); 201 | PQputf(param, "%complex[]", &complex_arr); 202 | PQparamClear(complex_arr.param); 203 | 204 | /* exec an insert */ 205 | res = PQparamExec(conn, param, "INSERT INTO t VALUES($1)", resfmt); 206 | PQparamClear(param); 207 | 208 | /* ------------------------- 209 | * Getting an array of composites 210 | */ 211 | 212 | int i; 213 | int ntups; 214 | PGfloat8 f8; 215 | PGint4 i4; 216 | PGtext textp; 217 | PGresult *simple; 218 | PGarray complex_arr; 219 | 220 | /* Get the complex[], provide a ptr to a PGarray. */ 221 | PQgetf(exec_result, 0, "%complex[]", 0, &complex_arr); 222 | 223 | /* no longer needed */ 224 | PQclear(exec_result); 225 | 226 | ntups = PQntuples(complex_arr.res); 227 | for(i=0; i < ntups; i++) 228 | { 229 | PQgetf(complex_arr.res, i, "%float8 %simple", 0, &f8, 1, &simple); 230 | 231 | /* Nested composites are like any other composite, tuple 0! Unless, 232 | * its a nested composite array. 233 | */ 234 | PQgetf(simple, 0, "#int4 #text", "a", &i4, "t", &textp); 235 | 236 | printf("(%f, (%d, %s))\\n", f8, i4, textp); 237 | PQclear(simple); 238 | } 239 | 240 | PQclear(complex_arr.res); 241 | \fP 242 | .RE 243 | .nf 244 | .SH EXAMPLES 245 | .LP 246 | None. 247 | .SH AUTHOR 248 | .LP 249 | A contribution of eSilo, LLC. for the PostgreSQL Database Management System. 250 | Written by Andrew Chernow and Merlin Moncure. 251 | .SH REPORTING BUGS 252 | .LP 253 | Report bugs to . 254 | .SH COPYRIGHT 255 | .LP 256 | Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 257 | .br 258 | This is free software; see the source for copying conditions. 259 | There is NO warranty; not even for MERCHANTABILITY or FITNESS 260 | FOR A PARTICULAR PURPOSE. 261 | .SH SEE ALSO 262 | .LP 263 | \fIPQgetf(3)\fP, \fIPQputf(3)\fP, \fIPQputvf(3)\fP 264 | -------------------------------------------------------------------------------- /source/groff2html: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################## 4 | # Converts a man page to very simple html, basically a page with 5 | # A and PRE tags. Takes 1 arguments: path to the non-compressed man 6 | # page. This expects the man page to be named PAGE_NAME.SECTION_NUMBER; 7 | # like `PQgetf.3' or `printf.1'. 8 | # 9 | # FEATURES 10 | # 1. Any some_man(3), no spaces before '(', is substituted for an 11 | # A tag linking to http://libpqtypes.esilo.com/man3/$man.html 12 | # 13 | # 2. All '<' and '>' are replaced with HTML codes 14 | # 15 | # 3. Mans referencing other mans, via .so macro, are converted 16 | # to symlinks in the output directory. 17 | # 18 | # ISSUES 19 | # Hyperlinks are not 100% substitued into resulting html page. 20 | # The only time there are issues is when the link text in the 21 | # man page was broken across lines. No work-around at this time 22 | # for this issue other than manually ensuring line breaks don't 23 | # occur on `some_man(3)'. 24 | # 25 | # Man pages that reference other man pages, using the .so macro, 26 | # must have the .so command in the first 'head' lines. The 27 | # .so 'path_to_man' cannot include spaces. 28 | ###################################################################### 29 | 30 | DOCTYPE="" 31 | 32 | # adjust these to your web server 33 | BASEURL="http:\/\/libpqtypes.esilo.com" 34 | INC_BASEURL="$BASEURL\/include" 35 | MAN_BASEURL="$BASEURL\/man" 36 | OUTDIR=/esilo/www/libpqtypes.esilo.com/man 37 | HEAD_SECTION="" 38 | HEADER="libpqtypes home page

" 39 | FOOTER="

libpqtypes home page" 40 | 41 | if [ $# = 0 ] ; then 42 | echo "Must supply man page to convert" 43 | exit 44 | fi 45 | 46 | # ignore non-existent files 47 | if test ! -f $1 ; then 48 | exit 49 | fi 50 | 51 | manpage=`basename $1` 52 | section=`echo "$manpage" | awk -F . '{print $NF}'` 53 | manpage=${manpage%.[^.]*} 54 | 55 | OUTDIR="${OUTDIR}${section}" 56 | mkdir -p $OUTDIR 57 | 58 | # If a .so reference, create a symlink 59 | solink=`head $1 | grep '.so .*' | cut -d ' ' -f 2` 60 | if test ! -z $solink ; then 61 | target=`basename $solink` 62 | target=${target%.[^.]*} 63 | echo "$OUTDIR/$manpage.html => $OUTDIR/$target.html" 64 | 65 | # remove existing symlink 66 | rm -f $OUTDIR/$manpage.html 67 | ln -s $OUTDIR/$target.html $OUTDIR/$manpage.html 68 | exit 69 | fi 70 | 71 | echo $OUTDIR/$manpage.html 72 | 73 | >$OUTDIR/$manpage.html 74 | 75 | # doctype 76 | if test ! -z "$DOCTYPE" ; then 77 | echo $DOCTYPE >>$OUTDIR/$manpage.html 78 | fi 79 | 80 | # add stadard html tags, include a title 81 | echo -e "\n\n$HEAD_SECTION\nman $manpage\n\n" >>$OUTDIR/$manpage.html 82 | 83 | if test ! -z "$HEADER" ; then 84 | echo $HEADER >>$OUTDIR/$manpage.html 85 | fi 86 | 87 | echo "

" >>$OUTDIR/$manpage.html
 88 | 
 89 | # 1. output man in ascii
 90 | # 2. post-process with col removing backspaces and tabs
 91 | # 3. do some html code replacement 
 92 | # 4. replace some_man(3) with A tags
 93 | groff -t -e -mandoc -Tascii $1 2>/dev/null | col -bx | \
 94 | sed 's//\>/g' | \
 95 | sed "s/\<\([a-ZA-Z0-9_\-]*\.h\)\>/\<\1<\/a>\>/g" | \
 96 | sed "s/\b\([a-zA-Z0-9_\-]\+\)(\([0-9]\))/\1(\2)<\/a>/g" \
 97 | >>$OUTDIR/$manpage.html
 98 | 
 99 | echo -e "
\n" >>$OUTDIR/$manpage.html 100 | 101 | if test ! -z "$FOOTER" ; then 102 | echo $FOOTER >>$OUTDIR/$manpage.html 103 | fi 104 | 105 | # html closing tags 106 | echo -e "\n" >>$OUTDIR/$manpage.html 107 | 108 | chown apache:apache $OUTDIR/$manpage.html 109 | 110 | -------------------------------------------------------------------------------- /source/reconf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # http://opendx.npaci.edu/mail/opendx-dev/2003.07/msg00081.html 4 | 5 | rm -f aclocal.m4 6 | 7 | CAT=false 8 | 9 | #You should add the contents of `/usr/local/share/aclocal/libtool15.m4' to `aclocal.m4'. 10 | OUT=$(libtoolize --force --copy | grep "You should add the contents of \`[^']*' to \`aclocal.m4'.") 11 | if [ $? == 0 ] ; then 12 | ACLOCAL=$(echo $OUT | sed "s/You should add the contents of \`//;s/' to \`aclocal.m4'.//") 13 | if [ $? == 0 ] ; then 14 | CAT=true 15 | fi 16 | fi 17 | 18 | aclocal 19 | if [ "$CAT" == "true" ] ; then 20 | cat $ACLOCAL >> aclocal.m4 21 | fi 22 | autoheader 23 | automake -a -i 24 | autoconf 25 | 26 | -------------------------------------------------------------------------------- /source/src/array.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * array.c 4 | * Type handler for the array data type. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | int 15 | pqt_put_array(PGtypeArgs *args) 16 | { 17 | int i; 18 | int hasnull=0; 19 | int ndims; 20 | int nitems; 21 | int arrsize; 22 | char *out; 23 | int lbound[MAXDIM]; 24 | int dims[MAXDIM]; 25 | PGarray *arr = va_arg(args->ap, PGarray *); 26 | 27 | PUTNULLCHK(args, arr); 28 | 29 | if (arr->ndims < 0) 30 | return args->errorf(args, "arr.ndims is invalid - %d", arr->ndims); 31 | 32 | /* auto configure when ndims is 0 to 1d array */ 33 | if (arr->ndims == 0) 34 | { 35 | ndims = 1; 36 | dims[0] = arr->param->vcnt; 37 | lbound[0] = 1; 38 | } 39 | else 40 | { 41 | ndims = arr->ndims; 42 | memcpy(lbound, arr->lbound, sizeof(lbound)); 43 | memcpy(dims, arr->dims, sizeof(dims)); 44 | } 45 | 46 | nitems = 1; 47 | for (i=0; i < ndims; i++) 48 | nitems *= dims[i]; 49 | 50 | /* make sure array is on the same page as the param */ 51 | if (nitems != arr->param->vcnt) 52 | return args->errorf(args, 53 | "param element count %d is different than array's %d", 54 | arr->param->vcnt, nitems); 55 | 56 | /* header: ndims + hasnull + elemtype + ((dims + lbound) * ndims) */ 57 | arrsize = 4 + 4 + 4 + (8 * ndims); 58 | 59 | /* compute data length, also get the hasnull flag */ 60 | for (i=0; i < arr->param->vcnt; i++) 61 | { 62 | if (arr->param->vals[i].format == 0) 63 | return args->errorf(args, "Cannot put array elements in text format"); 64 | 65 | arrsize += 4; 66 | if (arr->param->vals[i].datal == NULL_LEN) 67 | hasnull = 1; 68 | else 69 | arrsize += arr->param->vals[i].datal; 70 | } 71 | 72 | /* make sure args->put.out is large enough */ 73 | if (args->put.expandBuffer(args, arrsize) == -1) 74 | return -1; 75 | 76 | out = args->put.out; 77 | 78 | /* number od dimensions */ 79 | pqt_buf_putint4(out, ndims); 80 | out += 4; 81 | 82 | /* array hasnull flag */ 83 | pqt_buf_putint4(out, hasnull); 84 | out += 4; 85 | 86 | /* array element oid */ 87 | pqt_buf_putint4(out, args->typhandler->typoid); 88 | out += 4; 89 | 90 | /* dims and lbound */ 91 | for (i=0; i < ndims; i++) 92 | { 93 | pqt_buf_putint4(out, dims[i]); 94 | out += 4; 95 | 96 | pqt_buf_putint4(out, lbound[i]); 97 | out += 4; 98 | } 99 | 100 | /* write the element lengths and data */ 101 | for (i=0; i < arr->param->vcnt; i++) 102 | { 103 | pqt_buf_putint4(out, arr->param->vals[i].datal); 104 | out += 4; 105 | 106 | if (arr->param->vals[i].datal > 0) 107 | { 108 | memcpy(out, arr->param->vals[i].data, arr->param->vals[i].datal); 109 | out += arr->param->vals[i].datal; 110 | } 111 | } 112 | 113 | return arrsize; 114 | } 115 | 116 | int 117 | pqt_get_array(PGtypeArgs *args) 118 | { 119 | int i,t; 120 | int vlen; 121 | int ntups; 122 | int nattrs; 123 | Oid elemoid; 124 | DECLVALUE(args); 125 | PGresult *res; 126 | int first_tup; 127 | PGarray *arr = va_arg(args->ap, PGarray *); 128 | 129 | CHKGETVALS(args, arr); 130 | 131 | if (args->format == TEXTFMT) 132 | return args->errorf(args, "array does not support text results"); 133 | 134 | /* number of dims */ 135 | arr->ndims = pqt_buf_getint4(value); 136 | value += 4; 137 | 138 | /* skip NULL flag */ 139 | value += 4; 140 | 141 | /* check the element oid */ 142 | elemoid = (Oid)pqt_buf_getint4(value); 143 | if (elemoid != args->typhandler->typoid) 144 | return args->errorf(args, 145 | "array element type %u is different than what server says %u", 146 | args->typhandler->typoid, elemoid); 147 | value += 4; 148 | 149 | /* arr dims and lbounds */ 150 | first_tup = 1; 151 | for (i=0, ntups=1; i < arr->ndims; i++) 152 | { 153 | arr->dims[i] = pqt_buf_getint4(value); 154 | value += 4; 155 | 156 | arr->lbound[i] = pqt_buf_getint4(value); 157 | value += 4; 158 | 159 | ntups *= arr->dims[i]; 160 | } 161 | 162 | /* This means ndims is zero because the above loop never iterated. */ 163 | if (i == 0) 164 | ntups = 0; 165 | 166 | /* numTuples is the number of array items 167 | * and numAttributes is 1 for non-composites. 168 | */ 169 | nattrs = (args->typhandler->nattrs > 0) ? args->typhandler->nattrs : 1; 170 | 171 | if (!(res = pqt_copyresult(args, nattrs))) 172 | RERR_MEM(args); 173 | 174 | for (t=0; t < ntups; t++) 175 | { 176 | /* get the value len */ 177 | vlen = pqt_buf_getint4(value); 178 | value += 4; 179 | 180 | /* Regular Array with 1 attr per tuple */ 181 | if (args->typhandler->nattrs == 0) 182 | { 183 | /* set the field value */ 184 | if (!PQsetvalue(res, t, 0, value, vlen)) 185 | { 186 | PQclear(res); 187 | return -1; 188 | } 189 | 190 | if (vlen > 0) 191 | value += vlen; 192 | 193 | continue; 194 | } 195 | 196 | /* ------------------------ 197 | * COMPOSITE/RECORD 198 | */ 199 | 200 | /* NULL compsoite array item, fill in attrs with NULL */ 201 | if (vlen == NULL_LEN) 202 | { 203 | int x; 204 | 205 | for (x=0; x < nattrs; x++) 206 | { 207 | if (!PQsetvalue(res, t, x, NULL, NULL_LEN)) 208 | { 209 | PQclear(res); 210 | return -1; 211 | } 212 | } 213 | 214 | /* move on to next tuple, done here. */ 215 | continue; 216 | } 217 | 218 | /* verify that server's attr count matches ours */ 219 | if (first_tup) 220 | { 221 | int attcnt = pqt_buf_getint4(value); 222 | 223 | /* watch for invalidation issues */ 224 | if (attcnt != nattrs) 225 | { 226 | PQclear(res); 227 | return args->errorf(args, 228 | "type handler attribute count is %d but server says it's %d", 229 | args->typhandler->nattrs, attcnt); 230 | } 231 | } 232 | 233 | /* skip attr count */ 234 | value += 4; 235 | 236 | /* composite attributes (record columns) */ 237 | for (i=0; i < nattrs; i++) 238 | { 239 | /* watch for invalidation issues */ 240 | if (first_tup && 241 | (Oid) pqt_buf_getint4(value) != args->typhandler->attDescs[i].attoid) 242 | { 243 | Oid server_oid = (Oid) pqt_buf_getint4(value); 244 | 245 | args->errorf(args, 246 | "type handler attribute OID is %u but server says it's %u", 247 | args->typhandler->attDescs[i].attoid, server_oid); 248 | 249 | PQclear(res); 250 | return -1; 251 | } 252 | 253 | /* skip oid */ 254 | value += 4; 255 | 256 | /* get the value length */ 257 | vlen = pqt_buf_getint4(value); 258 | value += 4; 259 | 260 | /* set the field value */ 261 | if (!PQsetvalue(res, t, i, value, vlen)) 262 | { 263 | PQclear(res); 264 | return -1; 265 | } 266 | 267 | if (vlen > 0) 268 | value += vlen; 269 | } 270 | 271 | first_tup = 0; 272 | } 273 | 274 | arr->res = res; 275 | return 0; 276 | } 277 | 278 | 279 | -------------------------------------------------------------------------------- /source/src/error.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * error.c 4 | * The functions in this file represent the libpqtypes error 5 | * system. It offers the API user the ability to set/get errors. 6 | * The error system uses a per-thread global error, implemented 7 | * using TLS (via declspec(thread) or pthread TLS). For 8 | * non-PQT_THREAD_SAFE builds, a static buffer is used. 9 | * 10 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 11 | * This is free software; see the source for copying conditions. There is 12 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 13 | * PURPOSE. 14 | */ 15 | 16 | #include "libpqtypes-int.h" 17 | 18 | /* Cannot use allocated memory for the pqterr_t members. This is 19 | * because windows has no way of freeing per thread allocated 20 | * memory automatically, other than DllMain which won't work for 21 | * a third-party library such as libpqtypes. All error messages 22 | * and fields are truncated to fit their buffers via pqt_strcpy(). 23 | */ 24 | typedef struct 25 | { 26 | /* Room for user message and a full libpq message */ 27 | char msg[1024 + 8192]; 28 | 29 | /* Error Fields (a little less than 8K for all) */ 30 | char severity[16]; 31 | char sqlstate[16]; 32 | char message_primary[2048]; 33 | char message_detail[1024]; 34 | char message_hint[512]; 35 | char stmt_position[16]; 36 | char internal_position[16]; 37 | char internal_query[2048]; 38 | char context[2048]; 39 | char source_file[256]; 40 | char source_line[16]; 41 | char source_function[80]; 42 | } pqterr_t; 43 | 44 | /* non thread-safe, use a static */ 45 | #ifndef PQT_THREAD_SAFE 46 | static pqterr_t lasterr = {{0}}; 47 | 48 | /* MSVC always uses __declspec(thread) */ 49 | #elif defined(PQT_MSVC) 50 | static __declspec(thread) pqterr_t lasterr = {0}; 51 | 52 | /* Unix-variant, MinGW or Cygwin. */ 53 | #else 54 | #include 55 | static pthread_key_t tlskey_lasterr; 56 | 57 | /* When the thread dies, this will get called. */ 58 | static void tls_free_lasterr(void *value) 59 | { 60 | if (value) 61 | { 62 | free(value); 63 | pthread_setspecific(tlskey_lasterr, NULL); 64 | 65 | } 66 | } 67 | 68 | /* called before main. This attribute is available since gcc 2.7.0. 69 | * It replaced the obsolete _init ... destructor replaced _fini. 70 | * We used to use pthread_once, but solaris doesn't support this 71 | * in older versions (its simply a stub returning 0, ouch!). 72 | */ 73 | void __attribute__((constructor)) __InItErRoRkEy__(void) 74 | { 75 | pthread_key_create(&tlskey_lasterr, tls_free_lasterr); 76 | } 77 | #endif 78 | 79 | static void 80 | vseterror(const char *format, va_list ap, int append); 81 | 82 | static pqterr_t * 83 | geterr(void) 84 | { 85 | pqterr_t *err = NULL; 86 | 87 | /* Non thread-safe mode or windows msvc. */ 88 | #if !defined(PQT_THREAD_SAFE) || defined(PQT_MSVC) 89 | err = &lasterr; 90 | 91 | /* systems requiring pthread TLS keys */ 92 | #else 93 | 94 | /* System is using FSU Threads, like SCO OpenServer 5, which 95 | * has minor prototype differences. 96 | */ 97 | # ifdef PTHREAD_FSU 98 | pthread_getspecific(tlskey_lasterr, (void **) &err); 99 | # else 100 | err = (pqterr_t *) pthread_getspecific(tlskey_lasterr); 101 | # endif 102 | 103 | if (!err) 104 | { 105 | err = (pqterr_t *) malloc(sizeof(pqterr_t)); 106 | if (!err) 107 | return NULL; 108 | memset(err, 0, sizeof(pqterr_t)); 109 | pthread_setspecific(tlskey_lasterr, err); 110 | } 111 | 112 | #endif 113 | 114 | return err; 115 | } 116 | 117 | char * 118 | PQgeterror(void) 119 | { 120 | static char _empty[1] = {0}; 121 | pqterr_t *err = geterr(); 122 | return err ? err->msg : _empty; 123 | } 124 | 125 | void 126 | PQseterror(const char *format, ...) 127 | { 128 | /* clear error message by passing in NULL, PQseterror(NULL).*/ 129 | if (!format) 130 | { 131 | pqterr_t *err = geterr(); 132 | 133 | if (err && *err->msg) 134 | { 135 | /* do not memset the entire pqterr_t structure, its around 20K and 136 | * causes performance problems when putting lots of array elements, 137 | * since PQputvf always clears the error PQseterror(NULL) before 138 | * attempting to put data. 139 | */ 140 | *err->msg = 0; 141 | *err->severity = 0; 142 | *err->sqlstate = 0; 143 | *err->message_primary = 0; 144 | *err->message_detail = 0; 145 | *err->message_hint = 0; 146 | *err->stmt_position = 0; 147 | *err->internal_position = 0; 148 | *err->internal_query = 0; 149 | *err->context = 0; 150 | *err->source_file = 0; 151 | *err->source_line = 0; 152 | *err->source_function = 0; 153 | } 154 | } 155 | else 156 | { 157 | va_list ap; 158 | va_start(ap, format); 159 | vseterror(format, ap, FALSE); 160 | } 161 | } 162 | 163 | char * 164 | PQgetErrorField(int fieldcode) 165 | { 166 | pqterr_t *err = geterr(); 167 | 168 | if (!err) 169 | return NULL; 170 | 171 | switch (fieldcode) 172 | { 173 | case PG_DIAG_SEVERITY: 174 | return err->severity; 175 | 176 | case PG_DIAG_SQLSTATE: 177 | return err->sqlstate; 178 | 179 | case PG_DIAG_MESSAGE_PRIMARY: 180 | return err->message_primary; 181 | 182 | case PG_DIAG_MESSAGE_DETAIL: 183 | return err->message_detail; 184 | 185 | case PG_DIAG_MESSAGE_HINT: 186 | return err->message_hint; 187 | 188 | case PG_DIAG_STATEMENT_POSITION: 189 | return err->stmt_position; 190 | 191 | case PG_DIAG_INTERNAL_POSITION: 192 | return err->internal_position; 193 | 194 | case PG_DIAG_INTERNAL_QUERY: 195 | return err->internal_query; 196 | 197 | case PG_DIAG_CONTEXT: 198 | return err->context; 199 | 200 | case PG_DIAG_SOURCE_FILE: 201 | return err->source_file; 202 | 203 | case PG_DIAG_SOURCE_LINE: 204 | return err->source_line; 205 | 206 | case PG_DIAG_SOURCE_FUNCTION: 207 | return err->source_function; 208 | 209 | default: 210 | return NULL; 211 | } 212 | } 213 | 214 | /* Used by pqt_setresultfields */ 215 | #define geterrfield(buf, name) do{ \ 216 | if ((value = PQresultErrorField(res, name))) \ 217 | pqt_strcpy(buf, sizeof(buf), value); \ 218 | else \ 219 | *buf = 0; \ 220 | }while (0) 221 | 222 | void 223 | pqt_setresultfields(const PGresult *res) 224 | { 225 | char *value; 226 | pqterr_t *err = geterr(); 227 | 228 | if (!err) 229 | return; 230 | 231 | geterrfield(err->severity, PG_DIAG_SEVERITY); 232 | geterrfield(err->sqlstate, PG_DIAG_SQLSTATE); 233 | geterrfield(err->message_primary, PG_DIAG_MESSAGE_PRIMARY); 234 | geterrfield(err->message_detail, PG_DIAG_MESSAGE_DETAIL); 235 | geterrfield(err->message_hint, PG_DIAG_MESSAGE_HINT); 236 | geterrfield(err->stmt_position, PG_DIAG_STATEMENT_POSITION); 237 | geterrfield(err->internal_position, PG_DIAG_INTERNAL_POSITION); 238 | geterrfield(err->internal_query, PG_DIAG_INTERNAL_QUERY); 239 | geterrfield(err->context, PG_DIAG_CONTEXT); 240 | geterrfield(err->source_file, PG_DIAG_SOURCE_FILE); 241 | geterrfield(err->source_line, PG_DIAG_SOURCE_LINE); 242 | geterrfield(err->source_function, PG_DIAG_SOURCE_FUNCTION); 243 | } 244 | 245 | /* errorf() callback for PGtypeArgs, see PQputf() and PQgetf(). 246 | * Always returns -1. 247 | */ 248 | int 249 | pqt_argserrorf(PGtypeArgs *args, const char *format, ...) 250 | { 251 | va_list ap; 252 | char fqtn[200]; 253 | 254 | if (!args || !format || !*format) 255 | return -1; 256 | 257 | pqt_fqtn(fqtn, sizeof(fqtn), args->typhandler->typschema, 258 | args->typhandler->typname); 259 | 260 | /* put the header */ 261 | PQseterror("%s[pos:%d] - ", fqtn, args->typpos); 262 | 263 | /* append message from type handler */ 264 | va_start(ap, format); 265 | vseterror(format, ap, TRUE); 266 | return -1; 267 | } 268 | 269 | static void 270 | vseterror(const char *format, va_list ap, int append) 271 | { 272 | int n; 273 | int curlen = 0; 274 | int size; 275 | va_list ap2; 276 | char *msg = NULL; 277 | pqterr_t *err = geterr(); 278 | 279 | if (!err) 280 | return; 281 | 282 | if (append) 283 | curlen = (int) strlen(err->msg); 284 | else 285 | *err->msg = 0; 286 | 287 | va_copy(ap2, ap); 288 | n = pqt_vsnprintf(err->msg + curlen, sizeof(err->msg) - curlen, format, ap2); 289 | va_end(ap2); 290 | 291 | if (n > -1) 292 | return; 293 | 294 | /* pqterr_t msg buffer is too small for the complete message. We have 295 | * use a temporary buffer to get a successful sprintf so we can 296 | * pqt_strcpy() the result; which truncates to fit. 297 | */ 298 | size = (int) sizeof(err->msg) * 2; 299 | if (!(msg = (char *) malloc(size))) 300 | return; 301 | 302 | while (1) 303 | { 304 | char *p; 305 | 306 | va_copy(ap2, ap); 307 | n = pqt_vsnprintf(msg + curlen, size - curlen, format, ap2); 308 | va_end(ap2); 309 | 310 | /* success */ 311 | if (n > -1) 312 | break; 313 | 314 | /* need more space */ 315 | n = size * 2; 316 | if (!(p = pqt_realloc(msg, n))) 317 | { 318 | /* we're here because sprintf failed, don't trust buffer contents */ 319 | *msg = 0; 320 | break; 321 | } 322 | 323 | msg = p; 324 | size = n; 325 | } 326 | 327 | pqt_strcpy(err->msg, sizeof(err->msg), msg); 328 | free(msg); 329 | } 330 | 331 | -------------------------------------------------------------------------------- /source/src/events.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * events.c 4 | * The libpq PGEventProc implementation. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | static PGtypeData * 15 | allocTypeData(PGconn *conn); 16 | 17 | static void 18 | freeTypeData(PGtypeData *typeData); 19 | 20 | /* Deprecated, use PQinitTypes instead */ 21 | int 22 | PQtypesRegister(PGconn *conn) 23 | { 24 | return PQinitTypes(conn); 25 | } 26 | 27 | int 28 | pqt_eventproc(PGEventId id, void *info, void *passThrough) 29 | { 30 | switch (id) 31 | { 32 | case PGEVT_REGISTER: 33 | { 34 | PGEventRegister *e = (PGEventRegister *) info; 35 | void *data = allocTypeData(e->conn); 36 | if (!data) 37 | return FALSE; 38 | PQsetInstanceData((PGconn *) e->conn, pqt_eventproc, data); 39 | break; 40 | } 41 | 42 | case PGEVT_CONNRESET: 43 | { 44 | /* No special handling for PGEVT_CONNRESET. Previously, types were 45 | * automatically re-registered but this idea fails miserably during 46 | * asynchronous resets. Yanked in favor of using the following 47 | * call sequence: PQresetXXX, PQclearTypes, PQregisterTypes. 48 | */ 49 | break; 50 | } 51 | 52 | case PGEVT_CONNDESTROY: 53 | { 54 | PGEventConnDestroy *e = (PGEventConnDestroy *) info; 55 | freeTypeData((PGtypeData *) PQinstanceData(e->conn, pqt_eventproc)); 56 | break; 57 | } 58 | 59 | case PGEVT_RESULTCREATE: 60 | { 61 | PGtypeData *resData; 62 | PGEventResultCreate *e = (PGEventResultCreate *) info; 63 | PGtypeData *connData = (PGtypeData *) PQinstanceData( 64 | e->conn, pqt_eventproc); 65 | 66 | if (!connData || !(resData = allocTypeData(e->conn))) 67 | return FALSE; 68 | 69 | /* copy type handlers from PGconn's typeData */ 70 | if (connData->typhcnt > 0) 71 | { 72 | resData->typhandlers = pqt_duphandlers( 73 | connData->typhandlers, connData->typhcnt); 74 | 75 | if (resData->typhandlers) 76 | resData->typhcnt = connData->typhcnt; 77 | } 78 | 79 | /* copy type specs from PGconn's typeData */ 80 | if (connData->typspeccnt > 0) 81 | { 82 | resData->typspecs = pqt_dupspecs( 83 | connData->typspecs, connData->typspeccnt); 84 | 85 | if (resData->typspecs) 86 | resData->typspeccnt = connData->typspeccnt; 87 | } 88 | 89 | PQresultSetInstanceData((PGresult *) e->result, pqt_eventproc, resData); 90 | break; 91 | } 92 | 93 | case PGEVT_RESULTCOPY: 94 | { 95 | PGtypeData *destData; 96 | PGEventResultCopy *e = (PGEventResultCopy *) info; 97 | PGtypeData *srcData = (PGtypeData *) PQresultInstanceData( 98 | e->src, pqt_eventproc); 99 | 100 | if (!srcData || !(destData = allocTypeData(NULL))) 101 | return FALSE; 102 | 103 | memcpy(&destData->fmtinfo, &srcData->fmtinfo, sizeof(PGtypeFormatInfo)); 104 | 105 | /* copy type handlers from PGresult's typeData */ 106 | if (srcData->typhcnt > 0) 107 | { 108 | destData->typhandlers = pqt_duphandlers( 109 | srcData->typhandlers, srcData->typhcnt); 110 | 111 | if (destData->typhandlers) 112 | destData->typhcnt = srcData->typhcnt; 113 | } 114 | 115 | /* copy type specs from PGresult's typeData */ 116 | if (srcData->typspeccnt > 0) 117 | { 118 | destData->typspecs = pqt_dupspecs( 119 | srcData->typspecs, srcData->typspeccnt); 120 | 121 | if (destData->typspecs) 122 | destData->typspeccnt = srcData->typspeccnt; 123 | } 124 | 125 | PQresultSetInstanceData(e->dest, pqt_eventproc, destData); 126 | break; 127 | } 128 | 129 | case PGEVT_RESULTDESTROY: 130 | { 131 | PGEventResultDestroy *e = (PGEventResultDestroy *) info; 132 | freeTypeData((PGtypeData *) PQresultInstanceData( 133 | e->result, pqt_eventproc)); 134 | break; 135 | } 136 | } 137 | 138 | return TRUE; 139 | } 140 | 141 | static PGtypeData * 142 | allocTypeData(PGconn *conn) 143 | { 144 | PGtypeData *typeData = (PGtypeData *) malloc(sizeof(PGtypeData)); 145 | 146 | if (typeData) 147 | { 148 | memset(typeData, 0, sizeof(PGtypeData)); 149 | 150 | /* get type formatting info from conn */ 151 | if (conn) 152 | pqt_getfmtinfo(conn, &typeData->fmtinfo); 153 | } 154 | 155 | return typeData; 156 | } 157 | 158 | static void 159 | freeTypeData(PGtypeData *typeData) 160 | { 161 | if (typeData) 162 | { 163 | pqt_cleartypes(typeData); 164 | 165 | pqt_freespecs(typeData->typspecs, typeData->typspeccnt); 166 | typeData->typspecs = NULL; 167 | typeData->typspeccnt = 0; 168 | typeData->typspecmax = 0; 169 | 170 | free(typeData); 171 | } 172 | } 173 | 174 | -------------------------------------------------------------------------------- /source/src/exec.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * exec.c 4 | * Query execution and data retrieval functions. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | /* 15 | * Each param value requires an oid, ptr and 2 ints (oid, value, length, 16 | * format). This makes the maximum size of a param 20 bytes. A stack size 17 | * of 4k would allow for 204 query params. If more params are needed, 18 | * heap memory is used. Needing more than 204 param values in a query 19 | * is very rare, although possible. 20 | */ 21 | #define PARAM_STACKSIZE 4096 22 | 23 | #define BUILD_ARRAYS(rettype) \ 24 | rettype r; \ 25 | char *buf = NULL; \ 26 | Oid *oids = NULL; \ 27 | char **vals = NULL; \ 28 | int *lens = NULL; \ 29 | int *fmts = NULL; \ 30 | int vcnt = 0; \ 31 | char stackbuffer[PARAM_STACKSIZE]; \ 32 | \ 33 | PQseterror(NULL); \ 34 | if (!conn) \ 35 | { \ 36 | PQseterror("PGconn cannot be NULL"); \ 37 | return (rettype)(0); \ 38 | } \ 39 | \ 40 | if (param) \ 41 | { \ 42 | buf = stackbuffer; \ 43 | if (!buildArrays(param, &buf, &oids, &vals, &lens, &fmts)) \ 44 | return (rettype) (0); \ 45 | vcnt = param->vcnt; \ 46 | } 47 | 48 | #define RETURN_RESULT \ 49 | if (param) \ 50 | { \ 51 | if (buf && buf != stackbuffer) \ 52 | free(buf); \ 53 | } \ 54 | return r 55 | 56 | static int 57 | buildArrays(PGparam *param, char **buf, Oid **oids, 58 | char ***vals, int **lens, int **fmts); 59 | 60 | static PGresult * 61 | copyExecError(PGconn *conn, PGresult *r); 62 | 63 | static const char * 64 | getCommand(PGconn *conn, PGparam *param, const char *command); 65 | 66 | static int 67 | _execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp); 68 | 69 | int 70 | PQgetf(const PGresult *res, int tup_num, const char *format, ...) 71 | { 72 | int n; 73 | va_list ap; 74 | 75 | va_start(ap, format); 76 | n = PQgetvf(res, tup_num, format, ap); 77 | va_end(ap); 78 | 79 | return n; 80 | } 81 | 82 | int 83 | PQgetvf(const PGresult *res, int tup_num, const char *format, va_list ap) 84 | { 85 | int r; 86 | PGtypeHandler *h; 87 | PGtypeArgs args; 88 | int typpos = 0; 89 | int flags; 90 | Oid ftype; 91 | PGtypeData *resData; 92 | PGtypeSpec *spec = NULL; 93 | char tmp[200]; 94 | 95 | PQseterror(NULL); 96 | 97 | if (!res) 98 | { 99 | PQseterror("PGresult cannot be NULL"); 100 | return FALSE; 101 | } 102 | 103 | if (!(resData = (PGtypeData *) PQresultInstanceData(res, pqt_eventproc))) 104 | { 105 | PQseterror("PGresult at %p has no event data", res); 106 | return FALSE; 107 | } 108 | 109 | va_copy(args.ap, ap); 110 | 111 | /* "@name" format, lookup typeSpec in cache */ 112 | if(format && *format == '@') 113 | { 114 | spec = pqt_getspec(resData->typspecs, resData->typspeccnt, format + 1); 115 | 116 | /* If we didn't find a type spec, this is an error. A format string 117 | * with a '@' as its first character is reserved. 118 | */ 119 | if (!spec) 120 | { 121 | PQseterror("No such prepared specifier name: '%s'", format + 1); 122 | va_end(args.ap); 123 | return FALSE; 124 | } 125 | } 126 | 127 | while (format && *format) 128 | { 129 | if (spec) 130 | { 131 | /* done, no more handlers in cached spec string. */ 132 | if (typpos == spec->idcnt) 133 | break; 134 | 135 | h = pqt_gethandlerbyid(resData->typhandlers, resData->typhcnt, 136 | spec->idlist[typpos]); 137 | 138 | /* should be an unusual, or a "will never happen", situation */ 139 | if (!h) 140 | { 141 | va_end(args.ap); 142 | PQseterror("Unknown type handler id at position %d", typpos+1); 143 | return FALSE; 144 | } 145 | 146 | flags = (int) spec->flags[typpos]; 147 | typpos++; 148 | } 149 | else 150 | { 151 | format = pqt_parse(format, resData->typhandlers, resData->typhcnt, 152 | NULL, 0, &h, NULL, &typpos, &flags); 153 | 154 | if (!format) 155 | { 156 | va_end(args.ap); 157 | return FALSE; 158 | } 159 | 160 | if (!h) 161 | continue; 162 | } 163 | 164 | if (flags & TYPFLAG_BYNAME) 165 | args.get.field_num = PQfnumber(res, va_arg(args.ap, const char *)); 166 | else 167 | args.get.field_num = va_arg(args.ap, int); 168 | 169 | /* simplify life for handlers by checking getvalue's return here. */ 170 | if (args.get.field_num < 0 || 171 | !PQgetvalue(res, tup_num, args.get.field_num)) 172 | { 173 | PQseterror( 174 | "Invalid tup_num[%d].field_num[%d] (position %d)", 175 | tup_num, args.get.field_num, typpos); 176 | va_end(args.ap); 177 | return FALSE; 178 | } 179 | 180 | ftype = PQftype(res, args.get.field_num); 181 | if (((flags & TYPFLAG_ARRAY) && ftype != h->typoid_array) || 182 | (!(flags & TYPFLAG_ARRAY) && ftype != h->typoid)) 183 | { 184 | Oid oid = (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid; 185 | PQseterror( 186 | "Trying to get type %u '%s' but server returned %u (position %d)", 187 | oid, pqt_fqtn(tmp, sizeof(tmp), h->typschema, h->typname), 188 | ftype, typpos); 189 | va_end(args.ap); 190 | return FALSE; 191 | } 192 | 193 | args.is_put = 0; 194 | args.get.result = (PGresult *) res; 195 | args.format = PQfformat(res, args.get.field_num); 196 | args.fmtinfo = &resData->fmtinfo; 197 | args.get.tup_num = tup_num; 198 | args.is_ptr = (flags & TYPFLAG_POINTER) ? 1 : 0; 199 | args.typpos = typpos; 200 | args.typhandler = h; 201 | args.errorf = pqt_argserrorf; 202 | args.super = pqt_argssuper; 203 | 204 | if (flags & TYPFLAG_ARRAY) 205 | r = pqt_get_array(&args); 206 | else 207 | r = h->typget(&args); 208 | 209 | if (r == -1) 210 | { 211 | va_end(args.ap); 212 | return FALSE; 213 | } 214 | } 215 | 216 | va_end(args.ap); 217 | 218 | return TRUE; 219 | } 220 | 221 | /* -------------------------------- 222 | * Exec and Send functions 223 | */ 224 | 225 | PGresult * 226 | PQexecf(PGconn *conn, const char *cmdspec, ...) 227 | { 228 | va_list ap; 229 | PGresult *res; 230 | 231 | va_start(ap, cmdspec); 232 | res = PQexecvf(conn, cmdspec, ap); 233 | va_end(ap); 234 | 235 | return res; 236 | } 237 | 238 | PGresult * 239 | PQexecvf(PGconn *conn, const char *cmdspec, va_list ap) 240 | { 241 | PGresult *res; 242 | (void) _execvf(conn, cmdspec, ap, &res); 243 | return res; 244 | } 245 | 246 | int 247 | PQsendf(PGconn *conn, const char *cmdspec, ...) 248 | { 249 | int n; 250 | va_list ap; 251 | 252 | va_start(ap, cmdspec); 253 | n = PQsendvf(conn, cmdspec, ap); 254 | va_end(ap); 255 | 256 | return n; 257 | } 258 | 259 | int 260 | PQsendvf(PGconn *conn, const char *cmdspec, va_list ap) 261 | { 262 | return _execvf(conn, cmdspec, ap, NULL); 263 | } 264 | 265 | PGresult * 266 | PQparamExec(PGconn *conn, PGparam *param, const char *command, 267 | int resultFormat) 268 | { 269 | BUILD_ARRAYS(PGresult *); 270 | 271 | command = getCommand(conn, param, command); 272 | if (!command) 273 | { 274 | r = NULL; 275 | } 276 | else 277 | { 278 | r = PQexecParams(conn, command, vcnt, oids, 279 | (const char *const * ) vals, lens, fmts, resultFormat); 280 | 281 | pqt_setresultfields(r); 282 | r = copyExecError(conn, r); 283 | } 284 | 285 | RETURN_RESULT; 286 | } 287 | 288 | int 289 | PQparamSendQuery(PGconn *conn, PGparam *param, const char *command, 290 | int resultFormat) 291 | { 292 | BUILD_ARRAYS(int); 293 | 294 | command = getCommand(conn, param, command); 295 | if (!command) 296 | { 297 | r = FALSE; 298 | } 299 | else 300 | { 301 | r = PQsendQueryParams(conn, command, vcnt, oids, 302 | (const char *const * ) vals, lens, fmts, resultFormat); 303 | 304 | if (!r) 305 | PQseterror("PGconn: %s", PQerrorMessage(conn)); 306 | } 307 | 308 | RETURN_RESULT; 309 | } 310 | 311 | PGresult * 312 | PQparamExecPrepared(PGconn *conn, PGparam *param, const char *stmtName, 313 | int resultFormat) 314 | { 315 | BUILD_ARRAYS(PGresult *); 316 | 317 | r = PQexecPrepared(conn, stmtName, vcnt, (const char *const * ) vals, 318 | lens, fmts, resultFormat); 319 | 320 | pqt_setresultfields(r); 321 | r = copyExecError(conn, r); 322 | 323 | RETURN_RESULT; 324 | } 325 | 326 | int 327 | PQparamSendQueryPrepared(PGconn *conn, PGparam *param, const char *stmtName, 328 | int resultFormat) 329 | { 330 | BUILD_ARRAYS(int); 331 | 332 | r = PQsendQueryPrepared(conn, stmtName, vcnt, 333 | (const char *const * ) vals, lens, fmts, resultFormat); 334 | 335 | if (!r) 336 | PQseterror("PGconn: %s", PQerrorMessage(conn)); 337 | 338 | RETURN_RESULT; 339 | } 340 | 341 | /* Called by PQexecvf and PQsendvf. When resp is NULL, PQparamSendQuery 342 | * is used to execute the command. Otherwise, PQparamExec is used. The 343 | * return value is always zero when resp is not NULL. When resp is NULL, 344 | * the return value is zero for error and non-zero for success (identical 345 | * to PQparamSendQuery). 346 | */ 347 | static int 348 | _execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp) 349 | { 350 | int retval = 0; 351 | size_t stmt_len=0; 352 | char buffer[8192]; /* could be larger these days but be conservative */ 353 | char *stmt = NULL; 354 | PGparam *param = NULL; 355 | 356 | if (resp) 357 | *resp = NULL; 358 | 359 | if(!conn) 360 | { 361 | PQseterror("PGconn cannot be NULL"); 362 | return FALSE; 363 | } 364 | 365 | if(!cmdspec || !*cmdspec) 366 | { 367 | PQseterror("cmdspec cannot be NULL or an empty string"); 368 | return FALSE; 369 | } 370 | 371 | /* No stmt buf required for preapred type specs */ 372 | if (*cmdspec != '@') 373 | { 374 | /* The resulting parameterized command is guarenteed to be smaller 375 | * than the cmdspec. When needed, enlarge stmt buf to cmdspec length. 376 | */ 377 | stmt_len = strlen(cmdspec) + 1; 378 | 379 | /* stack buffer is too small, use heap */ 380 | if (stmt_len > sizeof(buffer)) 381 | { 382 | if (!(stmt = (char *) malloc(stmt_len))) 383 | { 384 | PQseterror(PQT_OUTOFMEMORY); 385 | return FALSE; 386 | } 387 | } 388 | else 389 | { 390 | stmt = buffer; 391 | stmt_len = sizeof(buffer); 392 | } 393 | } 394 | 395 | if ((param = PQparamCreate(conn))) 396 | { 397 | if (PQputvf(param, stmt, stmt_len, cmdspec, ap)) 398 | { 399 | const char *s = stmt ? stmt : cmdspec; 400 | 401 | if (resp) 402 | *resp = PQparamExec(conn, param, s, 1); 403 | else 404 | retval = PQparamSendQuery(conn, param, s, 1); 405 | } 406 | 407 | PQparamClear(param); 408 | } 409 | 410 | if (stmt && stmt != buffer) 411 | free(stmt); 412 | 413 | return retval; 414 | } 415 | 416 | /* 417 | * Assigns param values to param arrays, for use with postgres 418 | * parameter API. 'buf' is expected to be PARAM_STACKSIZE bytes. If more 419 | * memory is required, memory is allocated and assigned to *buf, which 420 | * must be freed by caller. To determine if *buf was allocated, compare 421 | * its address to the initially provided stack address. 422 | * Returns 1 on success and 0 on error. 423 | */ 424 | static int 425 | buildArrays(PGparam *param, char **buf, Oid **oids, 426 | char ***vals, int **lens, int **fmts) 427 | { 428 | int n; 429 | 430 | /* no params to assign */ 431 | if (param->vcnt == 0) 432 | return 1; 433 | 434 | /* required memory size for the 4 param arrays */ 435 | n = (int) ((sizeof(void *) * param->vcnt) + /* values */ 436 | ((sizeof(int) * 2) * param->vcnt) + /* lengths and formats */ 437 | (sizeof(Oid) * param->vcnt)); /* oids */ 438 | 439 | /* required memory is too large for stack buffer, get some heap */ 440 | if (n > PARAM_STACKSIZE) 441 | { 442 | char *p; 443 | 444 | if (!(p = (char *) malloc(n))) 445 | { 446 | PQseterror(PQT_OUTOFMEMORY); 447 | return 0; 448 | } 449 | 450 | *buf = p; 451 | } 452 | 453 | /* give arrays memory from buffer, which could be stack or heap. */ 454 | *vals = (char **) *buf; 455 | *lens = (int *) (*buf + (sizeof(void *) * param->vcnt)); 456 | *fmts = (*lens) + param->vcnt; 457 | *oids = (Oid *) ((*fmts) + param->vcnt); 458 | 459 | /* loop param values and assign value, length, format 460 | * and oid to arrays. 461 | */ 462 | for (n=0; n < param->vcnt; n++) 463 | { 464 | (*oids)[n] = param->vals[n].oid; 465 | (*vals)[n] = param->vals[n].data; 466 | (*lens)[n] = param->vals[n].datal; 467 | (*fmts)[n] = param->vals[n].format; 468 | } 469 | 470 | return 1; 471 | } 472 | 473 | static PGresult * 474 | copyExecError(PGconn *conn, PGresult *r) 475 | { 476 | if (!r) 477 | { 478 | PQseterror("PGconn: %s", PQerrorMessage(conn)); 479 | return NULL; 480 | } 481 | 482 | switch (PQresultStatus(r)) 483 | { 484 | case PGRES_COMMAND_OK: 485 | case PGRES_TUPLES_OK: 486 | case PGRES_EMPTY_QUERY: 487 | break; 488 | 489 | default: 490 | { 491 | PQseterror("PGresult: %s", PQresultErrorMessage(r)); 492 | PQclear(r); 493 | r = NULL; 494 | break; 495 | } 496 | } 497 | 498 | return r; 499 | } 500 | 501 | /* Using the param is preferred when both conn and param are provided. 502 | * The conn is there in case the exec has no parameters, NULL param. 503 | */ 504 | static const char * 505 | getCommand(PGconn *conn, PGparam *param, const char *command) 506 | { 507 | PGtypeSpec *spec; 508 | int typspeccnt = 0; 509 | PGtypeSpec *typspecs = NULL; 510 | 511 | if (!command) 512 | { 513 | PQseterror("command to execute cannot be NULL"); 514 | return NULL; 515 | } 516 | 517 | if (*command != '@') 518 | return command; 519 | 520 | if (param) 521 | { 522 | typspecs = param->typspecs; 523 | typspeccnt = param->typspeccnt; 524 | } 525 | 526 | /* Try to get instance data from the conn */ 527 | if (!typspecs || typspeccnt == 0) 528 | { 529 | PGtypeData *data = PQinstanceData(conn, pqt_eventproc); 530 | 531 | if (!data) 532 | { 533 | PQseterror("PGconn at %p has no event data", conn); 534 | return NULL; 535 | } 536 | 537 | typspecs = data->typspecs; 538 | typspeccnt = data->typspeccnt; 539 | } 540 | 541 | spec = pqt_getspec(typspecs, typspeccnt, command + 1); 542 | 543 | /* If we didn't find a type spec, this is an error. A format string 544 | * with an '@' as its first character is reserved. 545 | */ 546 | if (!spec) 547 | { 548 | PQseterror("No such prepared specifier name: '%s'", command + 1); 549 | return NULL; 550 | } 551 | 552 | /* make sure type spec was prepared with a statement */ 553 | if (!spec->stmt || !*spec->stmt) 554 | { 555 | PQseterror("Prepared specifier name '%s' has no statement", command + 1); 556 | return NULL; 557 | } 558 | 559 | return (const char *) spec->stmt; 560 | } 561 | 562 | 563 | -------------------------------------------------------------------------------- /source/src/geo.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * geo.c 4 | * Type handler for the geometric data types. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | static int text2point(PGpoint *pt, char *text, char **endptr); 15 | static int text2points(PGtypeArgs *args, PGpoint **pts, int *npts); 16 | static int bin2points(PGtypeArgs *args, char *valp, int ptcnt, 17 | PGpoint **pts, int *npts); 18 | static int putpoints(PGtypeArgs *args, int npts, PGpoint *pts, 19 | int is_path, int closed); 20 | 21 | int 22 | pqt_put_point(PGtypeArgs *args) 23 | { 24 | unsigned int *buf; 25 | PGpoint *pt = va_arg(args->ap, PGpoint *); 26 | 27 | PUTNULLCHK(args, pt); 28 | 29 | buf = (unsigned int *) args->put.out; 30 | pqt_swap8(buf, &pt->x, 1); 31 | pqt_swap8(buf + 2, &pt->y, 1); 32 | return 16; 33 | } 34 | 35 | int 36 | pqt_get_point(PGtypeArgs *args) 37 | { 38 | DECLVALUE(args); 39 | PGpoint *pt = va_arg(args->ap, PGpoint *); 40 | 41 | CHKGETVALS(args, pt); 42 | 43 | if (args->format == TEXTFMT) 44 | { 45 | if (!text2point(pt, value, NULL)) 46 | RERR_STR2INT(args); 47 | 48 | return 0; 49 | } 50 | 51 | pqt_swap8(&pt->x, (unsigned int *) value, 0); 52 | pqt_swap8(&pt->y, ((unsigned int *) (value)) + 2, 0); 53 | return 0; 54 | } 55 | 56 | int 57 | pqt_put_lseg(PGtypeArgs *args) 58 | { 59 | unsigned int *buf; 60 | PGlseg *lseg = va_arg(args->ap, PGlseg *); 61 | 62 | PUTNULLCHK(args, lseg); 63 | 64 | buf = (unsigned int *) args->put.out; 65 | pqt_swap8(buf, &lseg->pts[0].x, 1); 66 | pqt_swap8(buf + 2, &lseg->pts[0].y, 1); 67 | pqt_swap8(buf + 4, &lseg->pts[1].x, 1); 68 | pqt_swap8(buf + 6, &lseg->pts[1].y, 1); 69 | return 32; 70 | } 71 | 72 | int 73 | pqt_get_lseg(PGtypeArgs *args) 74 | { 75 | DECLVALUE(args); 76 | unsigned int *v; 77 | PGlseg *lseg = va_arg(args->ap, PGlseg *); 78 | 79 | CHKGETVALS(args, lseg); 80 | 81 | if (args->format == TEXTFMT) 82 | { 83 | PGpoint *pts = (PGpoint *)lseg; 84 | 85 | if (*value++ != '[' || 86 | !text2point(pts, value, &value) || 87 | *value++ != ',' || 88 | !text2point(pts + 1, value, &value) || 89 | *value != ']') 90 | RERR_STR2INT(args); 91 | 92 | return 0; 93 | } 94 | 95 | v = (unsigned int *) value; 96 | pqt_swap8(&lseg->pts[0].x, v, 0); 97 | pqt_swap8(&lseg->pts[0].y, v + 2, 0); 98 | pqt_swap8(&lseg->pts[1].x, v + 4, 0); 99 | pqt_swap8(&lseg->pts[1].y, v + 6, 0); 100 | return 0; 101 | } 102 | 103 | int 104 | pqt_put_box(PGtypeArgs *args) 105 | { 106 | unsigned int *buf; 107 | PGbox *box = va_arg(args->ap, PGbox *); 108 | 109 | PUTNULLCHK(args, box); 110 | 111 | buf = (unsigned int *) args->put.out; 112 | pqt_swap8(buf, &box->high.x, 1); 113 | pqt_swap8(buf + 2, &box->high.y, 1); 114 | pqt_swap8(buf + 4, &box->low.x, 1); 115 | pqt_swap8(buf + 6, &box->low.y, 1); 116 | return 32; 117 | } 118 | 119 | int 120 | pqt_get_box(PGtypeArgs *args) 121 | { 122 | DECLVALUE(args); 123 | unsigned int *v; 124 | PGbox *box = va_arg(args->ap, PGbox *); 125 | 126 | CHKGETVALS(args, box); 127 | 128 | if (args->format == TEXTFMT) 129 | { 130 | PGpoint *pts = (PGpoint *)box; 131 | 132 | if (!text2point(pts, value, &value) || 133 | *value++ != ',' || 134 | !text2point(pts + 1, value, NULL)) 135 | RERR_STR2INT(args); 136 | 137 | return 0; 138 | } 139 | 140 | v = (unsigned int *) value; 141 | pqt_swap8(&box->high.x, v, 0); 142 | pqt_swap8(&box->high.y, v + 2, 0); 143 | pqt_swap8(&box->low.x, v + 4, 0); 144 | pqt_swap8(&box->low.y, v + 6, 0); 145 | return 0; 146 | } 147 | 148 | int 149 | pqt_put_circle(PGtypeArgs *args) 150 | { 151 | unsigned int *buf; 152 | PGcircle *circle = va_arg(args->ap, PGcircle *); 153 | 154 | PUTNULLCHK(args, circle); 155 | 156 | buf = (unsigned int *) args->put.out; 157 | pqt_swap8(buf, &circle->center.x, 1); 158 | pqt_swap8(buf + 2, &circle->center.y, 1); 159 | pqt_swap8(buf + 4, &circle->radius, 1); 160 | return 24; 161 | } 162 | 163 | int 164 | pqt_get_circle(PGtypeArgs *args) 165 | { 166 | DECLVALUE(args); 167 | unsigned int *v; 168 | PGcircle *circle = va_arg(args->ap, PGcircle *); 169 | 170 | CHKGETVALS(args, circle); 171 | 172 | if (args->format == TEXTFMT) 173 | { 174 | if (*value++ != '<' || 175 | !text2point((PGpoint *)circle, value, &value) || 176 | *value++ != ',' || 177 | !pqt_text_to_float8(&circle->radius, value, &value) || 178 | *value != '>') 179 | RERR_STR2INT(args); 180 | 181 | return 0; 182 | } 183 | 184 | v = (unsigned int *) value; 185 | pqt_swap8(&circle->center.x, v, 0); 186 | pqt_swap8(&circle->center.y, v + 2, 0); 187 | pqt_swap8(&circle->radius, v + 4, 0); 188 | return 0; 189 | } 190 | 191 | int 192 | pqt_put_path(PGtypeArgs *args) 193 | { 194 | PGpath *path = va_arg(args->ap, PGpath *); 195 | PUTNULLCHK(args, path); 196 | return putpoints(args, path->npts, path->pts, 1, path->closed ? 1 : 0); 197 | } 198 | 199 | int 200 | pqt_get_path(PGtypeArgs *args) 201 | { 202 | DECLVALUE(args); 203 | PGpath *path = va_arg(args->ap, PGpath *); 204 | 205 | CHKGETVALS(args, path); 206 | 207 | if (args->format == TEXTFMT) 208 | { 209 | path->closed = *value == '(' ? 1 : 0; 210 | return text2points(args, &path->pts, &path->npts); 211 | } 212 | 213 | path->closed = *value ? 1 : 0; 214 | value++; 215 | 216 | return bin2points(args, 217 | value + sizeof(int), /* beginning of point array */ 218 | pqt_buf_getint4(value), /* number of points */ 219 | &path->pts, &path->npts); 220 | } 221 | 222 | int 223 | pqt_put_polygon(PGtypeArgs *args) 224 | { 225 | PGpolygon *polygon = va_arg(args->ap, PGpolygon *); 226 | PUTNULLCHK(args, polygon); 227 | return putpoints(args, polygon->npts, polygon->pts, 0, 0); 228 | } 229 | 230 | int 231 | pqt_get_polygon(PGtypeArgs *args) 232 | { 233 | DECLVALUE(args); 234 | PGpolygon *polygon = va_arg(args->ap, PGpolygon *); 235 | 236 | CHKGETVALS(args, polygon); 237 | 238 | if (args->format == TEXTFMT) 239 | return text2points(args, &polygon->pts, &polygon->npts); 240 | 241 | return bin2points(args, 242 | value + sizeof(int), /* beginning of point array */ 243 | pqt_buf_getint4(value), /* number of points */ 244 | &polygon->pts, 245 | &polygon->npts); 246 | } 247 | 248 | 249 | static int 250 | putpoints(PGtypeArgs *args, int npts, PGpoint *pts, 251 | int is_path, int closed) 252 | { 253 | int i; 254 | int datal; 255 | int hdr = (int) sizeof(int); 256 | char *out; 257 | 258 | /* pts is for a path, include 1 byte open/closed flag */ 259 | if (is_path) 260 | hdr++; 261 | 262 | /* length of binary formated path */ 263 | datal = (npts * sizeof(PGpoint)) + hdr; 264 | 265 | /* make sure out is large enough */ 266 | if (args->put.expandBuffer(args, datal) == -1) 267 | return -1; 268 | 269 | out = args->put.out; 270 | if (is_path) 271 | *out++ = closed ? 1 : 0; /* path open/closed flag */ 272 | 273 | /* write the number of points as an int32 */ 274 | pqt_buf_putint4(out, npts); 275 | out += 4; 276 | 277 | /* assign points to the data 'out' buffer */ 278 | for (i=0; i < npts; i++) 279 | { 280 | pqt_swap8(out, &pts[i].x, 1); 281 | out += sizeof(double); 282 | 283 | pqt_swap8(out, &pts[i].y, 1); 284 | out += sizeof(double); 285 | } 286 | 287 | return datal; 288 | } 289 | 290 | static int 291 | text2points(PGtypeArgs *args, PGpoint **pts, int *npts) 292 | { 293 | DECLVALUE(args); 294 | char *s; 295 | int cnt = 0; 296 | PGpoint *p = NULL; 297 | 298 | *pts = NULL; 299 | *npts = 0; 300 | 301 | if (*value != '(' && *value != '[') 302 | RERR(args, "malformed point array"); 303 | 304 | /* get the number of points by counting the '(' */ 305 | for (s=value+1; *s; s++) 306 | { 307 | if (*s == '(') 308 | { 309 | if (!(s = strchr(s, ')'))) /* skip point contents */ 310 | break; 311 | cnt++; 312 | } 313 | } 314 | 315 | if (cnt == 0) 316 | return 0; /* empty point list */ 317 | 318 | p = (PGpoint *) PQresultAlloc((PGresult *) args->get.result, 319 | cnt * sizeof(PGpoint)); 320 | if (!p) 321 | RERR_MEM(args); 322 | 323 | for (cnt=0; *++value; ) 324 | { 325 | if (!text2point(&p[cnt++], value, &value)) 326 | RERR_STR2INT(args); 327 | 328 | /* done */ 329 | if (*value != ',') 330 | break; 331 | } 332 | 333 | *pts = p; 334 | *npts = cnt; 335 | return 0; 336 | } 337 | 338 | static int 339 | bin2points(PGtypeArgs *args, char *valp, int ptcnt, 340 | PGpoint **pts, int *npts) 341 | { 342 | int i; 343 | PGpoint *p; 344 | 345 | *pts = NULL; 346 | *npts = 0; 347 | 348 | if (ptcnt == 0) 349 | return 0; 350 | 351 | p = (PGpoint *) PQresultAlloc((PGresult *) args->get.result, 352 | ptcnt * sizeof(PGpoint)); 353 | 354 | if (!p) 355 | RERR_MEM(args); 356 | 357 | for (i=0; i < ptcnt; i++) 358 | { 359 | pqt_swap8(&p[i].x, valp, 0); 360 | valp += sizeof(double); 361 | 362 | pqt_swap8(&p[i].y, valp, 0); 363 | valp += sizeof(double); 364 | } 365 | 366 | *pts = p; 367 | *npts = ptcnt; 368 | return 0; 369 | } 370 | 371 | static int 372 | text2point(PGpoint *pt, char *text, char **endptr) 373 | { 374 | if (*text++ != '(') 375 | return 0; 376 | 377 | if (!pqt_text_to_float8(&pt->x, text, &text) || *text++ != ',') 378 | return 0; 379 | 380 | if (!pqt_text_to_float8(&pt->y, text, &text) || *text++ != ')') 381 | return 0; 382 | 383 | if (endptr) 384 | *endptr = text; 385 | return 1; 386 | } 387 | 388 | 389 | 390 | 391 | 392 | -------------------------------------------------------------------------------- /source/src/getaddrinfo.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2003-2008, PostgreSQL Global Development Group 4 | * $PostgreSQL: pgsql/src/include/getaddrinfo.h 5 | */ 6 | 7 | #if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \ 8 | !defined(HAVE_GETADDRINFO)) 9 | 10 | #ifndef GETADDRINFO_H 11 | #define GETADDRINFO_H 12 | 13 | #include 14 | #define _XOPEN_SOURCE_EXTENDED 1 15 | #include 16 | 17 | 18 | /* Various macros that ought to be in , but might not be */ 19 | 20 | #ifndef EAI_FAIL 21 | #define EAI_BADFLAGS (-1) 22 | #define EAI_NONAME (-2) 23 | #define EAI_AGAIN (-3) 24 | #define EAI_FAIL (-4) 25 | #define EAI_FAMILY (-6) 26 | #define EAI_SOCKTYPE (-7) 27 | #define EAI_SERVICE (-8) 28 | #define EAI_MEMORY (-10) 29 | #define EAI_SYSTEM (-11) 30 | #endif /* !EAI_FAIL */ 31 | 32 | #ifndef AI_PASSIVE 33 | #define AI_PASSIVE 0x0001 34 | #endif 35 | 36 | #ifndef AI_NUMERICHOST 37 | /* 38 | * some platforms don't support AI_NUMERICHOST; define as zero if using 39 | * the system version of getaddrinfo... 40 | */ 41 | #if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO) 42 | #define AI_NUMERICHOST 0 43 | #else 44 | #define AI_NUMERICHOST 0x0004 45 | #endif 46 | #endif 47 | 48 | #ifndef NI_NUMERICHOST 49 | #define NI_NUMERICHOST 1 50 | #endif 51 | #ifndef NI_NUMERICSERV 52 | #define NI_NUMERICSERV 2 53 | #endif 54 | 55 | #ifndef NI_MAXHOST 56 | #define NI_MAXHOST 1025 57 | #endif 58 | #ifndef NI_MAXSERV 59 | #define NI_MAXSERV 32 60 | #endif 61 | 62 | #ifdef HAVE_STRUCT_SOCKADDR_STORAGE 63 | 64 | #ifndef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 65 | #ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY 66 | #define ss_family __ss_family 67 | #else 68 | #error struct sockaddr_storage does not provide an ss_family member 69 | #endif 70 | #endif 71 | 72 | #ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN 73 | #define ss_len __ss_len 74 | #define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 75 | #endif 76 | #else /* !HAVE_STRUCT_SOCKADDR_STORAGE */ 77 | 78 | /* Define a struct sockaddr_storage if we don't have one. */ 79 | 80 | #ifndef __CYGWIN__ 81 | struct sockaddr_storage 82 | { 83 | union 84 | { 85 | struct sockaddr sa; /* get the system-dependent fields */ 86 | long long int ss_align; /* ensures struct is properly aligned */ 87 | char ss_pad[128]; /* ensures struct has desired size */ 88 | } ss_stuff; 89 | }; 90 | #endif 91 | 92 | #define ss_family ss_stuff.sa.sa_family 93 | /* It should have an ss_len field if sockaddr has sa_len. */ 94 | #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 95 | #define ss_len ss_stuff.sa.sa_len 96 | #define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 97 | #endif 98 | #endif /* HAVE_STRUCT_SOCKADDR_STORAGE */ 99 | 100 | #ifndef HAVE_STRUCT_ADDRINFO 101 | 102 | struct addrinfo 103 | { 104 | int ai_flags; 105 | int ai_family; 106 | int ai_socktype; 107 | int ai_protocol; 108 | size_t ai_addrlen; 109 | struct sockaddr *ai_addr; 110 | char *ai_canonname; 111 | struct addrinfo *ai_next; 112 | }; 113 | 114 | #endif /* HAVE_STRUCT_ADDRINFO */ 115 | 116 | 117 | 118 | /* Rename private copies per comments above */ 119 | #ifdef getaddrinfo 120 | #undef getaddrinfo 121 | #endif 122 | 123 | #ifdef freeaddrinfo 124 | #undef freeaddrinfo 125 | #endif 126 | 127 | #ifdef gai_strerror 128 | #undef gai_strerror 129 | #endif 130 | 131 | #ifdef getnameinfo 132 | #undef getnameinfo 133 | #endif 134 | 135 | extern int getaddrinfo(const char *node, const char *service, 136 | const struct addrinfo * hints, struct addrinfo ** res); 137 | extern void freeaddrinfo(struct addrinfo * res); 138 | extern const char *gai_strerror(int errcode); 139 | extern int getnameinfo(const struct sockaddr * sa, int salen, 140 | char *node, int nodelen, 141 | char *service, int servicelen, int flags); 142 | 143 | #endif /* GETADDRINFO_H */ 144 | 145 | #endif /* HAVE_GETADDRINFO */ 146 | 147 | -------------------------------------------------------------------------------- /source/src/libpqtypes.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * libpqtypes.h 4 | * Public header for libpqtypes. Contains the entire public API. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #ifndef LIBPQTYPES_H 13 | #define LIBPQTYPES_H 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__) 24 | # define PQT_EXPORT __declspec(dllexport) 25 | #else 26 | # define PQT_EXPORT extern 27 | #endif 28 | 29 | /* MSVC 6 must use `i64', everything else uses `LL'. */ 30 | #if !defined(_MSC_VER) || _MSC_VER > 1200 31 | # define PQT_INT64CONST(x) ((PGint8) x##LL) 32 | #else 33 | # define PQT_INT64CONST(x) ((PGint8) x##i64) 34 | #endif 35 | 36 | enum 37 | { 38 | PQT_SUBCLASS, 39 | PQT_COMPOSITE, 40 | PQT_USERDEFINED 41 | }; 42 | 43 | typedef struct pg_param PGparam; 44 | typedef struct pg_typeargs PGtypeArgs; 45 | typedef int (*PGtypeProc)(PGtypeArgs *args); 46 | 47 | /* For use with a PQregisterXXX function */ 48 | typedef struct 49 | { 50 | const char *typname; 51 | PGtypeProc typput; 52 | PGtypeProc typget; 53 | } PGregisterType; 54 | 55 | typedef struct 56 | { 57 | int sversion; 58 | int pversion; 59 | char datestyle[48]; 60 | int integer_datetimes; 61 | } PGtypeFormatInfo; 62 | 63 | /* Record Attribute Description, its columns */ 64 | typedef struct 65 | { 66 | Oid attoid; 67 | int attlen; 68 | int atttypmod; 69 | char attname[65]; 70 | } PGrecordAttDesc; 71 | 72 | /* Type handler for putf and getf functions. The char fixed length buffers 73 | * used to be allocated pointers. This was a performance problem when 74 | * many type handlers are registered and one uses getf on a composite or 75 | * an array. These types require generating a PGresult and duplicating 76 | * the type handlers. Saved 40% by not having to deep copy the strings. 77 | */ 78 | typedef struct pg_typhandler 79 | { 80 | int id; 81 | char typschema[65]; 82 | char typname[65]; 83 | int typlen; 84 | Oid typoid; 85 | Oid typoid_array; 86 | PGtypeProc typput; 87 | PGtypeProc typget; 88 | int base_id; 89 | 90 | /* For composites, contains each attribute of a composite */ 91 | int nattrs; 92 | int freeAttDescs; 93 | PGrecordAttDesc attDescsBuf[16]; 94 | PGrecordAttDesc *attDescs; 95 | } PGtypeHandler; 96 | 97 | /* Values required during a type handler put ot get operation. */ 98 | struct pg_typeargs 99 | { 100 | int is_put; 101 | const PGtypeFormatInfo *fmtinfo; 102 | int is_ptr; 103 | int format; 104 | va_list ap; 105 | int typpos; 106 | PGtypeHandler *typhandler; 107 | int (*errorf)(PGtypeArgs *args, const char *format, ...); 108 | int (*super)(PGtypeArgs *args, ...); 109 | 110 | struct 111 | { 112 | PGparam *param; 113 | char *out; 114 | char *__allocated_out; /* leave me alone! */ 115 | int outl; 116 | int (*expandBuffer)(PGtypeArgs *args, int new_len); 117 | } put; 118 | 119 | struct 120 | { 121 | PGresult *result; 122 | int tup_num; 123 | int field_num; 124 | } get; 125 | }; 126 | 127 | 128 | /* ---------------- 129 | * Variable Length types 130 | * ---------------- 131 | */ 132 | 133 | typedef char *PGtext; 134 | typedef char *PGvarchar; 135 | typedef char *PGbpchar; 136 | typedef char *PGuuid; 137 | typedef struct 138 | { 139 | int len; 140 | char *data; 141 | } PGbytea; 142 | 143 | /* ---------------- 144 | * Numeric types 145 | * ---------------- 146 | */ 147 | 148 | typedef signed char PGchar; 149 | typedef int PGbool; 150 | typedef short PGint2; 151 | typedef int PGint4; 152 | typedef float PGfloat4; 153 | typedef double PGfloat8; 154 | typedef char *PGnumeric; 155 | 156 | /* Defined by an end-user if the system is missing long long. */ 157 | #ifdef PQT_LONG_LONG 158 | typedef PQT_LONG_LONG PGint8; 159 | typedef PQT_LONG_LONG PGmoney; 160 | 161 | /* MinGW and MSVC can use __int64 */ 162 | #elif defined(__MINGW32__) || defined(_MSC_VER) 163 | typedef __int64 PGint8; 164 | typedef __int64 PGmoney; 165 | 166 | /* Cygwin and Unixes. */ 167 | #else 168 | typedef long long PGint8; 169 | typedef long long PGmoney; 170 | #endif 171 | 172 | /* ---------------- 173 | * Geometric type structures 174 | * ---------------- 175 | */ 176 | 177 | typedef struct 178 | { 179 | double x; 180 | double y; 181 | } PGpoint; 182 | 183 | typedef struct 184 | { 185 | PGpoint pts[2]; 186 | } PGlseg; 187 | 188 | typedef struct 189 | { 190 | PGpoint high; 191 | PGpoint low; 192 | } PGbox; 193 | 194 | typedef struct 195 | { 196 | PGpoint center; 197 | double radius; 198 | } PGcircle; 199 | 200 | typedef struct 201 | { 202 | int npts; 203 | int closed; 204 | PGpoint *pts; /* for getf, only valid while PGresult is. */ 205 | } PGpath; 206 | 207 | typedef struct 208 | { 209 | int npts; 210 | PGpoint *pts; /* for getf, only valid while PGresult is. */ 211 | } PGpolygon; 212 | 213 | /* ---------------- 214 | * Network type structures 215 | * ---------------- 216 | */ 217 | 218 | /* This struct works with CIDR as well. */ 219 | typedef struct 220 | { 221 | int mask; 222 | int is_cidr; 223 | int sa_buf_len; 224 | 225 | /* sockaddr buffer, can be casted to sockaddr, sockaddr_in, 226 | * sockaddr_in6 or sockaddr_stroage. 227 | */ 228 | char sa_buf[128]; 229 | } PGinet; 230 | 231 | typedef struct 232 | { 233 | int a; 234 | int b; 235 | int c; 236 | int d; 237 | int e; 238 | int f; 239 | } PGmacaddr; 240 | 241 | /* ---------------- 242 | * Date & Time structures 243 | * ---------------- 244 | */ 245 | 246 | typedef struct 247 | { 248 | int years; 249 | int mons; 250 | int days; 251 | int hours; 252 | int mins; 253 | int secs; 254 | int usecs; 255 | } PGinterval; 256 | 257 | typedef struct 258 | { 259 | int isbc; 260 | int year; 261 | int mon; 262 | int mday; 263 | int jday; 264 | int yday; 265 | int wday; 266 | } PGdate; 267 | 268 | typedef struct 269 | { 270 | int hour; 271 | int min; 272 | int sec; 273 | int usec; 274 | int withtz; 275 | int isdst; 276 | int gmtoff; 277 | char tzabbr[16]; 278 | } PGtime; 279 | 280 | typedef struct 281 | { 282 | PGint8 epoch; 283 | PGdate date; 284 | PGtime time; 285 | } PGtimestamp; 286 | 287 | /* ---------------- 288 | * Array structures 289 | * ---------------- 290 | */ 291 | 292 | #ifndef MAXDIM 293 | # define MAXDIM 6 294 | #endif 295 | 296 | typedef struct 297 | { 298 | int ndims; 299 | int lbound[MAXDIM]; 300 | int dims[MAXDIM]; 301 | PGparam *param; 302 | PGresult *res; 303 | } PGarray; 304 | 305 | /* ---------------- 306 | * Public API funcs 307 | * ---------------- 308 | */ 309 | 310 | /* === in events.c === */ 311 | 312 | /* Deprecated, see PQinitTypes */ 313 | PQT_EXPORT int 314 | PQtypesRegister(PGconn *conn); 315 | 316 | /* === in error.c === */ 317 | 318 | PQT_EXPORT char * 319 | PQgeterror(void); 320 | 321 | /* PQseterror(NULL) will clear the error message */ 322 | PQT_EXPORT void 323 | PQseterror(const char *format, ...); 324 | 325 | /* Gets the error field for the last executed query. This only 326 | * pertains to PQparamExec and PQparamExecPrepared. When using a 327 | * standard libpq function like PQexec, PQresultErrorField should be used. 328 | */ 329 | PQT_EXPORT char * 330 | PQgetErrorField(int fieldcode); 331 | 332 | /* === in spec.c === */ 333 | 334 | /* Set 'format' argument to NULL to clear a single prepared specifier. */ 335 | PQT_EXPORT int 336 | PQspecPrepare(PGconn *conn, const char *name, const char *format, int is_stmt); 337 | 338 | PQT_EXPORT int 339 | PQclearSpecs(PGconn *conn); 340 | 341 | /* === in handler.c === */ 342 | 343 | /* Initialize type support on the given connection */ 344 | PQT_EXPORT int 345 | PQinitTypes(PGconn *conn); 346 | 347 | /* Deprecated, see PQregisterTypes */ 348 | PQT_EXPORT int 349 | PQregisterSubClasses(PGconn *conn, PGregisterType *types, int count); 350 | 351 | /* Deprecated, see PQregisterTypes */ 352 | PQT_EXPORT int 353 | PQregisterComposites(PGconn *conn, PGregisterType *types, int count); 354 | 355 | /* Deprecated, see PQregisterTypes */ 356 | PQT_EXPORT int 357 | PQregisterUserDefinedTypes(PGconn *conn, PGregisterType *types, int count); 358 | 359 | /* Registers PQT_SUBCLASS, PQT_COMPOSITE or PQT_USERDEFINED 360 | * (the 'which' argument) for use with libpqtypes. 361 | * 362 | * For asynchronous type registration, set the 'async' argument to a 363 | * non-zero value. This value is ignored when 'which' is PQT_SUBCLASS, 364 | * since subclass registration does not execute any commands against the 365 | * server. Use the standard PQconsumeInput, PQisBusy and PQgetResult 366 | * to properly obtain a PGresult, which must be passed to PQregisterResult 367 | * to complete the registration. 368 | */ 369 | PQT_EXPORT int 370 | PQregisterTypes(PGconn *conn, int which, PGregisterType *types, 371 | int count, int async); 372 | 373 | /* Registers a set of 'which' types found in the given PGresult. Caller 374 | * is responsible for clearing the result 'res'. Useful for performing 375 | * asynchronous type registration or for caching type result data to 376 | * avoid lookups on a new connection. If PQregisterTypes is ran in async 377 | * mode, the PGresult obtained via PGgetResult can be cached by an 378 | * application and provided to this function for new connections. 379 | * 380 | * Types and count should be identical to what was originally supplied 381 | * to PQregisterTypes. 382 | * 383 | * NOTE: although a PGconn is a required argument, it is never used 384 | * to perform any network operation (non-blocking safe). 385 | * 386 | * PQT_SUBCLASS is not supported and will result in an error if supplied. 387 | */ 388 | PQT_EXPORT int 389 | PQregisterResult(PGconn *conn, int which, PGregisterType *types, 390 | int count, PGresult *res); 391 | 392 | /* Clears all type handlers registered on 'conn'. This is useful after a 393 | * PQreset or PQresetPoll to optionally allow one to re-register types via 394 | * PQregisterTypes. 395 | */ 396 | PQT_EXPORT int 397 | PQclearTypes(PGconn *conn); 398 | 399 | /* === in param.c === */ 400 | 401 | PQT_EXPORT PGparam * 402 | PQparamCreate(const PGconn *conn); 403 | 404 | PQT_EXPORT PGparam * 405 | PQparamDup(PGparam *param); 406 | 407 | PQT_EXPORT int 408 | PQparamCount(PGparam *param); 409 | 410 | PQT_EXPORT void 411 | PQparamReset(PGparam *param); 412 | 413 | PQT_EXPORT void 414 | PQparamClear(PGparam *param); 415 | 416 | PQT_EXPORT int 417 | PQputf(PGparam *param, const char *format, ...); 418 | 419 | PQT_EXPORT int 420 | PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen, 421 | const char *format, va_list ap); 422 | 423 | /* === in exec.c === */ 424 | 425 | PQT_EXPORT int 426 | PQgetf(const PGresult *res, int tup_num, const char *format, ...); 427 | 428 | PQT_EXPORT int 429 | PQgetvf(const PGresult *res, int tup_num, const char *format, va_list ap); 430 | 431 | PQT_EXPORT PGresult * 432 | PQexecf(PGconn *conn, const char *cmdspec, ...); 433 | 434 | PQT_EXPORT PGresult * 435 | PQexecvf(PGconn *conn, const char *cmdspec, va_list ap); 436 | 437 | PQT_EXPORT int 438 | PQsendf(PGconn *conn, const char *cmdspec, ...); 439 | 440 | PQT_EXPORT int 441 | PQsendvf(PGconn *conn, const char *cmdspec, va_list ap); 442 | 443 | PQT_EXPORT PGresult * 444 | PQparamExec(PGconn *conn, PGparam *param, 445 | const char *command, int resultFormat); 446 | 447 | PQT_EXPORT int 448 | PQparamSendQuery(PGconn *conn, PGparam *param, 449 | const char *command, int resultFormat); 450 | 451 | PQT_EXPORT PGresult * 452 | PQparamExecPrepared(PGconn *conn, PGparam *param, 453 | const char *stmtName, int resultFormat); 454 | 455 | PQT_EXPORT int 456 | PQparamSendQueryPrepared(PGconn *conn, PGparam *param, 457 | const char *stmtName, int resultFormat); 458 | 459 | /* === in datetime.c === */ 460 | 461 | PQT_EXPORT void 462 | PQlocalTZInfo(time_t *t, int *gmtoff, int *isdst, char **tzabbrp); 463 | 464 | #ifdef __cplusplus 465 | } 466 | #endif 467 | #endif /* !LIBPQTYPES_H */ 468 | 469 | -------------------------------------------------------------------------------- /source/src/misc.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * misc.c 4 | * Type handler for CHAR, BOOL, MONEY, UUID data types. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | int 15 | pqt_put_char(PGtypeArgs *args) 16 | { 17 | PGchar c; 18 | 19 | if (args->is_ptr) 20 | { 21 | PGchar *ptr = (PGchar *) va_arg(args->ap, PGchar *); 22 | PUTNULLCHK(args, ptr); 23 | c = *ptr; 24 | } 25 | else 26 | { 27 | c = (PGchar) va_arg(args->ap, int); 28 | } 29 | 30 | *args->put.out = c; 31 | return 1; 32 | } 33 | 34 | int 35 | pqt_get_char(PGtypeArgs *args) 36 | { 37 | DECLVALUE(args); 38 | PGchar *chp = va_arg(args->ap, PGchar *); 39 | CHKGETVALS(args, chp); 40 | *chp = *value; 41 | return 0; 42 | } 43 | 44 | int 45 | pqt_put_bool(PGtypeArgs *args) 46 | { 47 | char b; 48 | 49 | if (args->is_ptr) 50 | { 51 | PGbool *ptr = (PGbool *) va_arg(args->ap, PGbool *); 52 | PUTNULLCHK(args, ptr); 53 | b = (char) *ptr; 54 | } 55 | else 56 | { 57 | b = (char) va_arg(args->ap, PGbool); 58 | } 59 | 60 | *args->put.out = b != 0 ? 1 : 0; 61 | return 1; 62 | } 63 | 64 | int 65 | pqt_get_bool(PGtypeArgs *args) 66 | { 67 | DECLVALUE(args); 68 | PGbool *boolp = va_arg(args->ap, PGbool *); 69 | 70 | CHKGETVALS(args, boolp); 71 | 72 | if (args->format == TEXTFMT) 73 | *boolp = (*value == 't') ? 1 : 0; 74 | else 75 | *boolp = (*value)!=0 ? 1 : 0; 76 | return 0; 77 | } 78 | 79 | int 80 | pqt_put_money(PGtypeArgs *args) 81 | { 82 | PGmoney money; 83 | int len = args->fmtinfo->sversion >= 80300 ? 8 : 4; 84 | 85 | if (args->is_ptr) 86 | { 87 | PGmoney *ptr = va_arg(args->ap, PGmoney *); 88 | PUTNULLCHK(args, ptr); 89 | money = *ptr; 90 | } 91 | else 92 | { 93 | money = va_arg(args->ap, PGmoney); 94 | } 95 | 96 | if (len == 8) 97 | pqt_swap8(args->put.out, &money, 1); 98 | else /* truncate: pre 8.3 server expecting a 4 byte money */ 99 | pqt_buf_putint4(args->put.out, (int) money); 100 | 101 | return len; 102 | } 103 | 104 | int 105 | pqt_get_money(PGtypeArgs *args) 106 | { 107 | DECLVALUE(args); 108 | DECLLENGTH(args); 109 | PGmoney *moneyp = va_arg(args->ap, PGmoney *); 110 | 111 | CHKGETVALS(args, moneyp); 112 | 113 | if (args->format == TEXTFMT) 114 | { 115 | char buf[64]; 116 | char c, *p = buf; 117 | char *bufend = buf + sizeof(buf); 118 | 119 | for (; (c = *value) != '\0' && pap, PGuuid); 151 | PUTNULLCHK(args, uuid); 152 | args->put.out = uuid; 153 | return 16; 154 | } 155 | 156 | int 157 | pqt_get_uuid(PGtypeArgs *args) 158 | { 159 | DECLVALUE(args); 160 | PGuuid *uuid = va_arg(args->ap, PGuuid *); 161 | 162 | CHKGETVALS(args, uuid); 163 | 164 | if (args->format == TEXTFMT) 165 | { 166 | int i; 167 | char buf[128]; 168 | char *p = buf; 169 | DECLLENGTH(args); 170 | 171 | /* format: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 */ 172 | for (i=0; i < valuel; i++) 173 | { 174 | if (value[i] != '-') 175 | { 176 | *p++ = (pqt_hex_to_dec(value[i]) << 4) | pqt_hex_to_dec(value[i+1]); 177 | i++; 178 | } 179 | } 180 | 181 | *uuid = (char *) PQresultAlloc(args->get.result, p - buf); 182 | if (!*uuid) 183 | RERR_MEM(args); 184 | 185 | memcpy(*uuid, buf, p - buf); 186 | return 0; 187 | } 188 | 189 | *uuid = value; 190 | return 0; 191 | } 192 | 193 | int 194 | pqt_put_null(PGtypeArgs *args) 195 | { 196 | args->put.out = NULL; 197 | return 0; 198 | } 199 | 200 | -------------------------------------------------------------------------------- /source/src/network.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * network.c 4 | * Type handler for the network data types. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | #if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \ 15 | !defined(HAVE_GETADDRINFO)) 16 | # include "getaddrinfo.h" 17 | #endif 18 | 19 | #ifndef PGSQL_AF_INET 20 | # define PGSQL_AF_INET (AF_INET + 0) 21 | #endif 22 | 23 | #ifndef PGSQL_AF_INET6 24 | # define PGSQL_AF_INET6 (AF_INET + 1) 25 | #endif 26 | 27 | #ifndef AF_INET6 28 | #warning NO AF_INET6 SUPPORT! 29 | #endif 30 | 31 | /* Some platforms don't define this, like AIX 4.3 */ 32 | #ifndef AI_NUMERICHOST 33 | # define AI_NUMERICHOST 0x04 34 | #endif 35 | 36 | /* handles cidr as well */ 37 | int 38 | pqt_put_inet(PGtypeArgs *args) 39 | { 40 | unsigned char *b = (unsigned char *)args->put.out; 41 | PGinet *inet = va_arg(args->ap, PGinet *); 42 | int family; 43 | 44 | PUTNULLCHK(args, inet); 45 | 46 | family = ((struct sockaddr *)inet->sa_buf)->sa_family; 47 | 48 | if (family == AF_INET) 49 | { 50 | struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf; 51 | *b++ = (unsigned char) PGSQL_AF_INET; 52 | *b++ = (unsigned char) inet->mask; 53 | *b++ = (unsigned char) (inet->is_cidr ? 1 : 0); 54 | *b++ = (unsigned char) 4; 55 | memcpy(b, &sa->sin_addr, 4); 56 | b += 4; 57 | } 58 | #ifdef AF_INET6 59 | else if (family == AF_INET6) 60 | { 61 | struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf; 62 | *b++ = (unsigned char) PGSQL_AF_INET6; 63 | *b++ = (unsigned char) inet->mask; 64 | *b++ = (unsigned char) (inet->is_cidr ? 1 : 0); 65 | *b++ = (unsigned char) 16; 66 | memcpy(b, &sa->sin6_addr, 16); 67 | b += 16; 68 | } 69 | #endif 70 | else 71 | { 72 | return args->errorf(args, "Unknown inet address family %d", family); 73 | } 74 | 75 | return (int) ((char *) b - args->put.out); 76 | } 77 | 78 | static int 79 | get_inet2(PGtypeArgs *args, int is_cidr) 80 | { 81 | DECLVALUE(args); 82 | unsigned short family; 83 | PGinet *inet = va_arg(args->ap, PGinet *); 84 | 85 | CHKGETVALS(args, inet); 86 | 87 | if (args->format == TEXTFMT) 88 | { 89 | int r; 90 | char *p; 91 | char ipstr[80]; 92 | struct addrinfo *ai = NULL; 93 | struct addrinfo hints; 94 | 95 | pqt_strcpy(ipstr, sizeof(ipstr), value); 96 | if ((p = strrchr(ipstr, '/'))) 97 | { 98 | *p = 0; 99 | inet->mask = atoi(p+1); 100 | } 101 | else 102 | { 103 | inet->mask = 32; 104 | } 105 | 106 | inet->is_cidr = is_cidr; 107 | 108 | /* suppress hostname lookups */ 109 | memset(&hints, 0, sizeof(hints)); 110 | hints.ai_flags = AI_NUMERICHOST; 111 | 112 | /* Without this, windows chokes with WSAHOST_NOT_FOUND */ 113 | #ifdef PQT_WIN32 114 | hints.ai_family = AF_INET; 115 | #endif 116 | 117 | if ((r = getaddrinfo(ipstr, NULL, &hints, &ai)) || !ai) 118 | { 119 | if(r == EAI_BADFLAGS) 120 | { 121 | hints.ai_flags = 0; 122 | r = getaddrinfo(ipstr, NULL, &hints, &ai); 123 | } 124 | /* Another WSAHOST_NOT_FOUND work around, but for IPv6 */ 125 | #if defined(PQT_WIN32) && defined(AF_INET6) 126 | else if(r == WSAHOST_NOT_FOUND) 127 | { 128 | hints.ai_flags = 0; 129 | hints.ai_family = AF_INET6; 130 | r = getaddrinfo(ipstr, NULL, &hints, &ai); 131 | } 132 | #endif 133 | 134 | if(r) 135 | RERR_STR2INT(args); 136 | } 137 | 138 | inet->sa_buf_len = (int) ai->ai_addrlen; 139 | memcpy(inet->sa_buf, ai->ai_addr, inet->sa_buf_len); 140 | 141 | /* Some platforms, lika AIX 4.3, do not zero this structure properly. 142 | * The family and port are dirty, so set the first 4 bytes to 0 and 143 | * then re-set the family. I saw "0x1002" as the first 2 bytes of 144 | * this structure (dumb getaddrinfo), it should be "0x0002" for AF_INET. 145 | */ 146 | memset(inet->sa_buf, 0, 4); 147 | ((struct sockaddr *)inet->sa_buf)->sa_family = ai->ai_addr->sa_family; 148 | 149 | /* Uninitialized memory, postgres inet/cidr types don't store this. 150 | * Make sure its set to 0. Another AIX problem (maybe other platforms). 151 | */ 152 | #ifdef AF_INET6 153 | if (ai->ai_addr->sa_family == AF_INET6) 154 | ((struct sockaddr_in6 *)inet->sa_buf)->sin6_flowinfo = 0; 155 | #endif 156 | 157 | freeaddrinfo(ai); 158 | return 0; 159 | } 160 | 161 | family = (unsigned short) *value++; 162 | if (family == PGSQL_AF_INET) 163 | { 164 | struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf; 165 | sa->sin_family = AF_INET; 166 | inet->mask = (unsigned char) *value++; 167 | inet->is_cidr = *value++; 168 | memcpy(&sa->sin_addr, value + 1, *value); 169 | inet->sa_buf_len = (int) sizeof(struct sockaddr_in); 170 | } 171 | #ifdef AF_INET6 172 | else if (family == PGSQL_AF_INET6) 173 | { 174 | struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf; 175 | sa->sin6_family = AF_INET6; 176 | inet->mask = (unsigned char) *value++; 177 | inet->is_cidr = *value++; 178 | memcpy(&sa->sin6_addr, value + 1, *value); 179 | inet->sa_buf_len = (int) sizeof(struct sockaddr_in6); 180 | } 181 | #endif 182 | else 183 | { 184 | return args->errorf(args, "Unknown inet address family %d", family); 185 | } 186 | 187 | return 0; 188 | } 189 | 190 | int 191 | pqt_get_inet(PGtypeArgs *args) 192 | { 193 | return get_inet2(args, 0); 194 | } 195 | 196 | int 197 | pqt_get_cidr(PGtypeArgs *args) 198 | { 199 | return get_inet2(args, 1); 200 | } 201 | 202 | int 203 | pqt_put_macaddr(PGtypeArgs *args) 204 | { 205 | PGmacaddr *mac = va_arg(args->ap, PGmacaddr *); 206 | 207 | PUTNULLCHK(args, mac); 208 | 209 | args->put.out[0] = (unsigned char) mac->a; 210 | args->put.out[1] = (unsigned char) mac->b; 211 | args->put.out[2] = (unsigned char) mac->c; 212 | args->put.out[3] = (unsigned char) mac->d; 213 | args->put.out[4] = (unsigned char) mac->e; 214 | args->put.out[5] = (unsigned char) mac->f; 215 | return 6; 216 | } 217 | 218 | int 219 | pqt_get_macaddr(PGtypeArgs *args) 220 | { 221 | DECLVALUE(args); 222 | unsigned char *p; 223 | PGmacaddr *mac = va_arg(args->ap, PGmacaddr *); 224 | 225 | CHKGETVALS(args, mac); 226 | 227 | if (args->format == TEXTFMT) 228 | { 229 | int a,b,c,d,e,f; 230 | int count = sscanf(value, "%x:%x:%x:%x:%x:%x", &a,&b,&c,&d,&e,&f); 231 | 232 | if (count != 6 || (a < 0) || (a > 255) || (b < 0) || (b > 255) || 233 | (c < 0) || (c > 255) || (d < 0) || (d > 255) || 234 | (e < 0) || (e > 255) || (f < 0) || (f > 255)) 235 | RERR_STR2INT(args); 236 | 237 | mac->a = a; 238 | mac->b = b; 239 | mac->c = c; 240 | mac->d = d; 241 | mac->e = e; 242 | mac->f = f; 243 | return 0; 244 | } 245 | 246 | p = (unsigned char *) value; 247 | mac->a = *p++; 248 | mac->b = *p++; 249 | mac->c = *p++; 250 | mac->d = *p++; 251 | mac->e = *p++; 252 | mac->f = *p; 253 | return 0; 254 | } 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /source/src/param.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * param.c 4 | * Management functions for the PGparam object. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | static int argsExpandBuffer(PGtypeArgs *args, int new_len); 15 | 16 | PGparam * 17 | PQparamCreate(const PGconn *conn) 18 | { 19 | PGparam *param; 20 | PGtypeData *connData; 21 | 22 | PQseterror(NULL); 23 | 24 | if (!conn) 25 | { 26 | PQseterror("PGconn cannot be NULL"); 27 | return NULL; 28 | } 29 | 30 | param = (PGparam *) malloc(sizeof(PGparam)); 31 | if (!param) 32 | { 33 | PQseterror(PQT_OUTOFMEMORY); 34 | return NULL; 35 | } 36 | 37 | memset(param, 0, sizeof(PGparam)); 38 | 39 | connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc); 40 | if (!connData) 41 | { 42 | PQparamClear(param); 43 | PQseterror("No type data exists for PGconn at %p", conn); 44 | return NULL; 45 | } 46 | 47 | /* copy any handlers from conn object */ 48 | if (connData->typhcnt > 0) 49 | { 50 | param->typhandlers = pqt_duphandlers( 51 | connData->typhandlers, connData->typhcnt); 52 | 53 | if (!param->typhandlers) 54 | { 55 | PQparamClear(param); 56 | PQseterror(PQT_OUTOFMEMORY); 57 | return NULL; 58 | } 59 | 60 | param->typhcnt = connData->typhcnt; 61 | } 62 | 63 | /* copy any typespecs from conn object */ 64 | if (connData->typspeccnt > 0) 65 | { 66 | param->typspecs = pqt_dupspecs( 67 | connData->typspecs, connData->typspeccnt); 68 | 69 | if (!param->typspecs) 70 | { 71 | PQparamClear(param); 72 | PQseterror(PQT_OUTOFMEMORY); 73 | return NULL; 74 | } 75 | 76 | param->typspeccnt = connData->typspeccnt; 77 | } 78 | 79 | pqt_getfmtinfo(conn, ¶m->fmtinfo); 80 | return param; 81 | } 82 | 83 | PQT_EXPORT PGparam * 84 | PQparamDup(PGparam *param) 85 | { 86 | PGparam *new_param; 87 | 88 | PQseterror(NULL); 89 | 90 | if (!param) 91 | { 92 | PQseterror("PGparam to duplicate cannot be NULL"); 93 | return NULL; 94 | } 95 | 96 | new_param = (PGparam *) malloc(sizeof(PGparam)); 97 | if (!new_param) 98 | { 99 | PQseterror(PQT_OUTOFMEMORY); 100 | return NULL; 101 | } 102 | 103 | memset(new_param, 0, sizeof(PGparam)); 104 | 105 | /* copy any handlers from conn object */ 106 | if (param->typhcnt > 0) 107 | { 108 | new_param->typhandlers = pqt_duphandlers( 109 | param->typhandlers, param->typhcnt); 110 | 111 | if (!new_param->typhandlers) 112 | { 113 | PQparamClear(new_param); 114 | PQseterror(PQT_OUTOFMEMORY); 115 | return NULL; 116 | } 117 | 118 | new_param->typhcnt = param->typhcnt; 119 | } 120 | 121 | /* copy any typespecs from conn object */ 122 | if (param->typspeccnt > 0) 123 | { 124 | new_param->typspecs = pqt_dupspecs( 125 | param->typspecs, param->typspeccnt); 126 | 127 | if (!new_param->typspecs) 128 | { 129 | PQparamClear(new_param); 130 | PQseterror(PQT_OUTOFMEMORY); 131 | return NULL; 132 | } 133 | 134 | new_param->typspeccnt = param->typspeccnt; 135 | } 136 | 137 | memcpy(&new_param->fmtinfo, ¶m->fmtinfo, sizeof(PGtypeFormatInfo)); 138 | 139 | /* copy any values, don't bother if array is empty. */ 140 | if (param->vcnt > 0) 141 | { 142 | int i; 143 | 144 | for (i=0; i < param->vcnt; i++) 145 | { 146 | int flags = 0; 147 | PGvalue *val = ¶m->vals[i]; 148 | 149 | /* Is val->data is direct user pointer? If so, set pointer flag 150 | * so pqt_putparam doesn't deep copy it. 151 | */ 152 | if (val->ptr != val->data) 153 | flags |= TYPFLAG_POINTER; 154 | 155 | if (!pqt_putparam(new_param, val->data, val->datal, flags, 156 | val->format, val->oid)) 157 | { 158 | PQparamClear(new_param); 159 | return NULL; 160 | } 161 | } 162 | } 163 | 164 | return new_param; 165 | } 166 | 167 | int 168 | PQparamCount(PGparam *param) 169 | { 170 | if (param) 171 | return param->vcnt; 172 | return 0; 173 | } 174 | 175 | void 176 | PQparamReset(PGparam *param) 177 | { 178 | if (param) 179 | param->vcnt = 0; 180 | } 181 | 182 | void 183 | PQparamClear(PGparam *param) 184 | { 185 | int i; 186 | 187 | if (!param) 188 | return; 189 | 190 | /* vmax is the size of the vals array. We don't use vcnt because 191 | * someone could of just called PQparamReset, leaving vcnt 0. If 192 | * we find any value pointers that are not NULL, free'm. 193 | */ 194 | for (i=0; i < param->vmax; i++) 195 | if (param->vals[i].ptr) 196 | free(param->vals[i].ptr); 197 | 198 | if (param->vals) 199 | free(param->vals); 200 | 201 | pqt_freehandlers(param->typhandlers, param->typhcnt); 202 | pqt_freespecs(param->typspecs, param->typspeccnt); 203 | 204 | param->vmax = 0; 205 | param->vcnt = 0; 206 | param->vals = NULL; 207 | param->typhcnt = 0; 208 | param->typhandlers = NULL; 209 | param->typspeccnt = 0; 210 | param->typspecs = NULL; 211 | 212 | free(param); 213 | } 214 | 215 | int 216 | PQputf(PGparam *param, const char *format, ...) 217 | { 218 | int r; 219 | va_list ap; 220 | 221 | /* This function is just a wrapper to PQputvf */ 222 | va_start(ap, format); 223 | r = PQputvf(param, NULL, 0, format, ap); 224 | va_end(ap); 225 | 226 | return r; 227 | } 228 | 229 | int 230 | PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen, 231 | const char *format, va_list ap) 232 | { 233 | int n=0; 234 | int flags; 235 | size_t stmtPos = 0; 236 | int save_vcnt; 237 | int typpos = 0; 238 | PGtypeHandler *h; 239 | PGtypeArgs args; 240 | char args_outbuf[4096]; 241 | PGtypeSpec *spec = NULL; 242 | 243 | PQseterror(NULL); 244 | 245 | if (!param) 246 | { 247 | PQseterror("PGparam cannot be NULL"); 248 | return FALSE; 249 | } 250 | 251 | if (!format || !*format) 252 | { 253 | PQseterror("param 'format' cannot be NULL or an empty string"); 254 | return FALSE; 255 | } 256 | 257 | if (stmtBuf && stmtBufLen < 1) 258 | { 259 | PQseterror("Invalid argument: stmtBufLen must be >= 1"); 260 | return FALSE; 261 | } 262 | 263 | save_vcnt = param->vcnt; 264 | va_copy(args.ap, ap); 265 | 266 | /* "@name" format, lookup typeSpec in cache */ 267 | if(format && *format == '@') 268 | { 269 | 270 | spec = pqt_getspec(param->typspecs, param->typspeccnt, format + 1); 271 | 272 | /* If we didn't find a type spec, this is an error. A format string 273 | * with a '@' as its first character is reserved. 274 | */ 275 | if (!spec) 276 | { 277 | PQseterror("No such prepared specifier name: '%s'", format + 1); 278 | return FALSE; 279 | } 280 | } 281 | 282 | while (format && *format) 283 | { 284 | if (spec) 285 | { 286 | /* done, no more handlers in cached spec string. */ 287 | if (typpos == spec->idcnt) 288 | break; 289 | 290 | h = pqt_gethandlerbyid(param->typhandlers, param->typhcnt, 291 | spec->idlist[typpos]); 292 | 293 | /* should be an unusual, or a "will never happen", situation */ 294 | if (!h) 295 | { 296 | va_end(args.ap); 297 | PQseterror("Unknown type handler id at position %d", typpos+1); 298 | param->vcnt = save_vcnt; 299 | return FALSE; 300 | } 301 | 302 | flags = (int) spec->flags[typpos]; 303 | typpos++; 304 | } 305 | else 306 | { 307 | format = pqt_parse(format, param->typhandlers, param->typhcnt, 308 | stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags); 309 | 310 | if (!format) 311 | { 312 | param->vcnt = save_vcnt; 313 | return FALSE; 314 | } 315 | 316 | if (!h) 317 | continue; 318 | } 319 | 320 | args.is_put = 1; 321 | args.put.param = param; 322 | args.fmtinfo = ¶m->fmtinfo; 323 | args.put.out = args_outbuf; 324 | args.put.__allocated_out = NULL; 325 | args.put.outl = (int) sizeof(args_outbuf); 326 | args.is_ptr = (flags & TYPFLAG_POINTER) ? 1 : 0; 327 | args.format = BINARYFMT; 328 | args.put.expandBuffer = argsExpandBuffer; 329 | args.typpos = typpos; 330 | args.typhandler = h; 331 | args.errorf = pqt_argserrorf; 332 | args.super = pqt_argssuper; 333 | *args.put.out = 0; 334 | 335 | if (flags & TYPFLAG_ARRAY) 336 | n = pqt_put_array(&args); 337 | else 338 | n = h->typput(&args); 339 | 340 | if (n == -1) 341 | { 342 | if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf) 343 | free(args.put.__allocated_out); 344 | param->vcnt = save_vcnt; 345 | return FALSE; 346 | } 347 | 348 | if (args.put.out == NULL) 349 | { 350 | args.format = BINARYFMT; 351 | n = -1; 352 | } 353 | 354 | n = pqt_putparam(param, args.put.out, n, flags, args.format, 355 | (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid); 356 | 357 | if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf) 358 | free(args.put.__allocated_out); 359 | 360 | if (!n) 361 | { 362 | param->vcnt = save_vcnt; 363 | return FALSE; 364 | } 365 | } 366 | 367 | if (stmtBuf) 368 | stmtBuf[stmtPos] = 0; 369 | 370 | return TRUE; 371 | } 372 | 373 | 374 | /* ---------------------------- 375 | * Helper functions 376 | */ 377 | 378 | int 379 | pqt_putparam(PGparam *param, const void *data, int datal, 380 | int flags, int format, Oid typoid) 381 | { 382 | PGvalue *v; 383 | 384 | if (!param) 385 | return FALSE; 386 | 387 | if (!data) 388 | datal = -1; 389 | 390 | /* need to grow param vals array */ 391 | if (param->vcnt == param->vmax) 392 | { 393 | PGvalue *vals; 394 | int vmax = param->vmax ? (param->vmax * 3) / 2 : 16; 395 | 396 | vals = (PGvalue *) pqt_realloc(param->vals, sizeof(PGvalue) * vmax); 397 | if (!vals) 398 | { 399 | PQseterror(PQT_OUTOFMEMORY); 400 | return FALSE; 401 | } 402 | 403 | /* zero out the new array elements */ 404 | memset(vals + param->vcnt, 0, (vmax - param->vcnt) * sizeof(PGvalue)); 405 | param->vmax = vmax; 406 | param->vals = vals; 407 | } 408 | 409 | /* grab next param value */ 410 | v = ¶m->vals[param->vcnt]; 411 | 412 | if (datal == -1) 413 | { 414 | v->data = NULL; 415 | } 416 | else if (datal == 0) 417 | { 418 | /* BUG FIX: allows putting empty strings in binary mode. Without this, 419 | * empty strings would be converted to an SQL NULL. 420 | * Reported by: Dmitry Epstein 421 | */ 422 | static char zero[1] = {0}; 423 | v->data = zero; 424 | } 425 | /* wants to put a direct pointer */ 426 | else if (flags & TYPFLAG_POINTER) 427 | { 428 | v->data = (char *) data; 429 | } 430 | else 431 | { 432 | /* need more mem for param value ptr */ 433 | if (v->ptrl < datal) 434 | { 435 | char *ptr = (char *) pqt_realloc(v->ptr, datal); 436 | 437 | if (!ptr) 438 | { 439 | PQseterror(PQT_OUTOFMEMORY); 440 | return FALSE; 441 | } 442 | 443 | v->ptrl = datal; 444 | v->ptr = ptr; 445 | } 446 | 447 | memcpy(v->ptr, data, datal); 448 | v->data = v->ptr; 449 | } 450 | 451 | v->datal = datal; 452 | v->format = format; 453 | v->oid = typoid; 454 | param->vcnt++; 455 | return TRUE; 456 | } 457 | 458 | /* returns 0 on success and -1 on error */ 459 | static int 460 | argsExpandBuffer(PGtypeArgs *args, int new_len) 461 | { 462 | char *new_out; 463 | 464 | if (new_len <= args->put.outl) 465 | return 0; 466 | 467 | if (!args->put.__allocated_out) 468 | { 469 | new_out = (char *) malloc(new_len); 470 | if (!new_out) 471 | return args->errorf(args, PQT_OUTOFMEMORY); 472 | 473 | /* fully copy existing stack buffer, we don't know what data 474 | * the user has already written and may want to keep. 475 | */ 476 | memcpy(new_out, args->put.out, args->put.outl); 477 | } 478 | else 479 | { 480 | new_out = (char *) realloc(args->put.__allocated_out, new_len); 481 | if (!new_out) 482 | return args->errorf(args, PQT_OUTOFMEMORY); 483 | } 484 | 485 | args->put.out = args->put.__allocated_out = new_out; 486 | args->put.outl = new_len; 487 | return 0; 488 | } 489 | 490 | 491 | 492 | 493 | 494 | -------------------------------------------------------------------------------- /source/src/port.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * port.c 4 | * Portability functions. Some of these functions are drop-ins for 5 | * systems missing them and others just centralize differences. 6 | * 7 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 8 | * This is free software; see the source for copying conditions. There is 9 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 10 | * PURPOSE. 11 | */ 12 | 13 | #include "libpqtypes-int.h" 14 | 15 | #if defined(__MINGW32__) || defined(__CYGWIN__) 16 | # define HAVE_VSNPRINTF 17 | #endif 18 | 19 | int 20 | pqt_snprintf(char *buf, size_t size, const char *format, ...) 21 | { 22 | int n; 23 | va_list ap; 24 | va_start(ap, format); 25 | n = pqt_vsnprintf(buf, size, format, ap); 26 | va_end(ap); 27 | return n; 28 | } 29 | 30 | int 31 | pqt_vsnprintf(char *buf, size_t size, const char *format, va_list ap) 32 | { 33 | int n; 34 | 35 | #ifdef PQT_MSVC 36 | # if PQT_MSVC >= 1400 37 | /* MSVC 8 */ 38 | n = _vsnprintf_s(buf, size, size-1, format, ap); 39 | # else 40 | /* MSVC 6 or 7 */ 41 | n = _vsnprintf(buf, size, format, ap); 42 | # endif 43 | 44 | #elif defined(HAVE_VSNPRINTF) 45 | /* All other platforms, including MinGW and Cygwin. */ 46 | n = vsnprintf(buf, size, format, ap); 47 | #else 48 | /* Some platforms don't have the buffer-safe version */ 49 | n = vsprintf(buf, format, ap); 50 | #endif 51 | 52 | if (n > -1 && (size_t) n < size) 53 | return n; 54 | 55 | /* Although some implementations return "required" buf size, this 56 | * always return -1 to keep things consistent for caller. 57 | */ 58 | return -1; 59 | } 60 | 61 | #if defined(HAVE_CONFIG_H) && (!defined(HAVE_STRTOL) || \ 62 | !defined(HAVE_STRTOUL)) 63 | 64 | static unsigned long string2long(const char *nptr, char **endptr, 65 | int base, int is_signed); 66 | 67 | #ifndef HAVE_STRTOL 68 | long 69 | strtol(const char *nptr, char **endptr, int base) 70 | { 71 | return (signed long) string2long(nptr, endptr, base, 1); 72 | } 73 | #endif 74 | 75 | #ifndef HAVE_STRTOUL 76 | unsigned long 77 | strtoul(const char *nptr, char **endptr, int base) 78 | { 79 | return (unsigned long) string2long(nptr, endptr, base, 0); 80 | } 81 | #endif 82 | 83 | #define between(a, c, z) \ 84 | ((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a))) 85 | 86 | static unsigned long 87 | string2long(const char *nptr, char ** const endptr, 88 | int base, int is_signed) 89 | { 90 | unsigned int v; 91 | unsigned long val = 0; 92 | int c; 93 | int ovfl = 0, sign = 1; 94 | const char *startnptr = nptr, *nrstart; 95 | 96 | if (endptr) 97 | *endptr = (char *) nptr; 98 | 99 | while (isspace(*nptr)) 100 | nptr++; 101 | c = *nptr; 102 | 103 | if (c == '-' || c == '+') 104 | { 105 | if (c == '-') 106 | sign = -1; 107 | nptr++; 108 | } 109 | nrstart = nptr; /* start of the number */ 110 | 111 | /* When base is 0, the syntax determines the actual base */ 112 | if (base == 0) 113 | { 114 | if (*nptr == '0') 115 | { 116 | if (*++nptr == 'x' || *nptr == 'X') 117 | { 118 | base = 16; 119 | nptr++; 120 | } 121 | else 122 | { 123 | base = 8; 124 | } 125 | } 126 | else 127 | { 128 | base = 10; 129 | } 130 | } 131 | else if (base==16 && *nptr=='0' && (*++nptr =='x' || *nptr =='X')) 132 | { 133 | nptr++; 134 | } 135 | 136 | for (;;) 137 | { 138 | c = *nptr; 139 | if (between('0', c, '9')) 140 | v = c - '0'; 141 | else if (between('a', c, 'z')) 142 | v = c - 'a' + 0xa; 143 | else if (between('A', c, 'Z')) 144 | v = c - 'A' + 0xA; 145 | else 146 | break; 147 | 148 | if (v >= base) 149 | break; 150 | if (val > (ULONG_MAX - v) / base) 151 | ovfl++; 152 | 153 | val = (val * base) + v; 154 | nptr++; 155 | } 156 | 157 | if (endptr) 158 | { 159 | if (nrstart == nptr) 160 | *endptr = (char *) startnptr; 161 | else 162 | *endptr = (char *) nptr; 163 | } 164 | 165 | if (!ovfl) 166 | { 167 | /* Overflow is only possible when converting a signed long. */ 168 | if (is_signed && ((sign < 0 && val > -(unsigned long) LONG_MIN) 169 | || (sign > 0 && val > LONG_MAX))) 170 | ovfl++; 171 | } 172 | 173 | if (ovfl) 174 | { 175 | errno = ERANGE; 176 | if (is_signed) 177 | { 178 | if (sign < 0) 179 | return LONG_MIN; 180 | else 181 | return LONG_MAX; 182 | } 183 | else 184 | { 185 | return ULONG_MAX; 186 | } 187 | } 188 | 189 | return (long) sign * val; 190 | } 191 | 192 | #endif /* !strtol || !strtoul */ 193 | 194 | 195 | /* Non-windows platforms that don't have strtoll */ 196 | #if defined(HAVE_CONFIG_H) && !defined(HAVE_STRTOLL) 197 | 198 | #define MIN_INT64 (-MAX_INT64 - PQT_INT64CONST(1)) 199 | #define MAX_INT64 PQT_INT64CONST(9223372036854775807) 200 | 201 | /* no locale support */ 202 | long long int 203 | strtoll(const char *nptr, char **endptr, int base) 204 | { 205 | const char *s; 206 | /* LONGLONG */ 207 | long long int acc, cutoff; 208 | int c; 209 | int neg, any, cutlim; 210 | 211 | /* endptr may be NULL */ 212 | 213 | #ifdef __GNUC__ 214 | /* This outrageous construct just to shut up a GCC warning. */ 215 | (void) &acc; (void) &cutoff; 216 | #endif 217 | 218 | /* 219 | * Skip white space and pick up leading +/- sign if any. 220 | * If base is 0, allow 0x for hex and 0 for octal, else 221 | * assume decimal; if base is already 16, allow 0x. 222 | */ 223 | s = nptr; 224 | do { 225 | c = (unsigned char) *s++; 226 | } while (isspace(c)); 227 | if (c == '-') { 228 | neg = 1; 229 | c = *s++; 230 | } else { 231 | neg = 0; 232 | if (c == '+') 233 | c = *s++; 234 | } 235 | if ((base == 0 || base == 16) && 236 | c == '0' && (*s == 'x' || *s == 'X')) { 237 | c = s[1]; 238 | s += 2; 239 | base = 16; 240 | } 241 | if (base == 0) 242 | base = c == '0' ? 8 : 10; 243 | 244 | /* 245 | * Compute the cutoff value between legal numbers and illegal 246 | * numbers. That is the largest legal value, divided by the 247 | * base. An input number that is greater than this value, if 248 | * followed by a legal input character, is too big. One that 249 | * is equal to this value may be valid or not; the limit 250 | * between valid and invalid numbers is then based on the last 251 | * digit. For instance, if the range for long longs is 252 | * [-9223372036854775808..9223372036854775807] and the input base 253 | * is 10, cutoff will be set to 922337203685477580 and cutlim to 254 | * either 7 (neg==0) or 8 (neg==1), meaning that if we have 255 | * accumulated a value > 922337203685477580, or equal but the 256 | * next digit is > 7 (or 8), the number is too big, and we will 257 | * return a range error. 258 | * 259 | * Set any if any `digits' consumed; make it negative to indicate 260 | * overflow. 261 | */ 262 | cutoff = neg ? MIN_INT64 : MAX_INT64; 263 | cutlim = (int) (cutoff % base); 264 | cutoff /= base; 265 | if (neg) { 266 | if (cutlim > 0) { 267 | cutlim -= base; 268 | cutoff += 1; 269 | } 270 | cutlim = -cutlim; 271 | } 272 | for (acc = 0, any = 0;; c = (unsigned char) *s++) { 273 | if (isdigit(c)) 274 | c -= '0'; 275 | else if (isalpha(c)) 276 | c -= isupper(c) ? 'A' - 10 : 'a' - 10; 277 | else 278 | break; 279 | if (c >= base) 280 | break; 281 | if (any < 0) 282 | continue; 283 | if (neg) { 284 | if (acc < cutoff || (acc == cutoff && c > cutlim)) { 285 | any = -1; 286 | acc = MIN_INT64; 287 | errno = ERANGE; 288 | } else { 289 | any = 1; 290 | acc *= base; 291 | acc -= c; 292 | } 293 | } else { 294 | if (acc > cutoff || (acc == cutoff && c > cutlim)) { 295 | any = -1; 296 | acc = MAX_INT64; 297 | errno = ERANGE; 298 | } else { 299 | any = 1; 300 | acc *= base; 301 | acc += c; 302 | } 303 | } 304 | } 305 | if (endptr != 0) 306 | /* LINTED interface specification */ 307 | *endptr = (char *) (any ? s - 1 : nptr); 308 | return (acc); 309 | } 310 | 311 | #endif /* !HAVE_STRTOLL */ 312 | 313 | 314 | /* Non-windows machines missing getaddrinfo (postgres's port) */ 315 | #if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \ 316 | !defined(HAVE_GETADDRINFO)) 317 | #undef FRONTEND 318 | #include 319 | #include 320 | #include 321 | #include 322 | 323 | #include "getaddrinfo.h" 324 | 325 | extern int h_errno; 326 | 327 | /* 328 | * get address info for ipv4 sockets. 329 | * 330 | * Bugs: - only one addrinfo is set even though hintp is NULL or 331 | * ai_socktype is 0 332 | * - AI_CANONNAME is not supported. 333 | * - servname can only be a number, not text. 334 | */ 335 | int 336 | getaddrinfo(const char *node, const char *service, 337 | const struct addrinfo * hintp, 338 | struct addrinfo ** res) 339 | { 340 | struct addrinfo *ai; 341 | struct sockaddr_in sin, 342 | *psin; 343 | struct addrinfo hints; 344 | 345 | if (hintp == NULL) 346 | { 347 | memset(&hints, 0, sizeof(hints)); 348 | hints.ai_family = AF_INET; 349 | hints.ai_socktype = SOCK_STREAM; 350 | } 351 | else 352 | memcpy(&hints, hintp, sizeof(hints)); 353 | 354 | if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) 355 | return EAI_FAMILY; 356 | 357 | if (hints.ai_socktype == 0) 358 | hints.ai_socktype = SOCK_STREAM; 359 | 360 | if (!node && !service) 361 | return EAI_NONAME; 362 | 363 | memset(&sin, 0, sizeof(sin)); 364 | 365 | sin.sin_family = AF_INET; 366 | 367 | if (node) 368 | { 369 | if (node[0] == '\0') 370 | sin.sin_addr.s_addr = htonl(INADDR_ANY); 371 | else if (hints.ai_flags & AI_NUMERICHOST) 372 | { 373 | if (!inet_aton(node, &sin.sin_addr)) 374 | return EAI_FAIL; 375 | } 376 | else 377 | { 378 | struct hostent *hp; 379 | 380 | #ifdef FRONTEND 381 | struct hostent hpstr; 382 | char buf[BUFSIZ]; 383 | int herrno = 0; 384 | 385 | pqGethostbyname(node, &hpstr, buf, sizeof(buf), 386 | &hp, &herrno); 387 | #else 388 | hp = gethostbyname(node); 389 | #endif 390 | if (hp == NULL) 391 | { 392 | switch (h_errno) 393 | { 394 | case HOST_NOT_FOUND: 395 | case NO_DATA: 396 | return EAI_NONAME; 397 | case TRY_AGAIN: 398 | return EAI_AGAIN; 399 | case NO_RECOVERY: 400 | default: 401 | return EAI_FAIL; 402 | } 403 | } 404 | if (hp->h_addrtype != AF_INET) 405 | return EAI_FAIL; 406 | 407 | memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length); 408 | } 409 | } 410 | else 411 | { 412 | if (hints.ai_flags & AI_PASSIVE) 413 | sin.sin_addr.s_addr = htonl(INADDR_ANY); 414 | else 415 | sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 416 | } 417 | 418 | if (service) 419 | sin.sin_port = htons((unsigned short) atoi(service)); 420 | 421 | #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 422 | sin.sin_len = sizeof(sin); 423 | #endif 424 | 425 | ai = malloc(sizeof(*ai)); 426 | if (!ai) 427 | return EAI_MEMORY; 428 | 429 | psin = malloc(sizeof(*psin)); 430 | if (!psin) 431 | { 432 | free(ai); 433 | return EAI_MEMORY; 434 | } 435 | 436 | memcpy(psin, &sin, sizeof(*psin)); 437 | 438 | ai->ai_flags = 0; 439 | ai->ai_family = AF_INET; 440 | ai->ai_socktype = hints.ai_socktype; 441 | ai->ai_protocol = hints.ai_protocol; 442 | ai->ai_addrlen = sizeof(*psin); 443 | ai->ai_addr = (struct sockaddr *) psin; 444 | ai->ai_canonname = NULL; 445 | ai->ai_next = NULL; 446 | 447 | *res = ai; 448 | 449 | return 0; 450 | } 451 | 452 | 453 | void 454 | freeaddrinfo(struct addrinfo * res) 455 | { 456 | if (res) 457 | { 458 | if (res->ai_addr) 459 | free(res->ai_addr); 460 | free(res); 461 | } 462 | } 463 | 464 | 465 | const char * 466 | gai_strerror(int errcode) 467 | { 468 | #ifdef HAVE_HSTRERROR 469 | int hcode; 470 | 471 | switch (errcode) 472 | { 473 | case EAI_NONAME: 474 | hcode = HOST_NOT_FOUND; 475 | break; 476 | case EAI_AGAIN: 477 | hcode = TRY_AGAIN; 478 | break; 479 | case EAI_FAIL: 480 | default: 481 | hcode = NO_RECOVERY; 482 | break; 483 | } 484 | 485 | return hstrerror(hcode); 486 | #else /* !HAVE_HSTRERROR */ 487 | 488 | switch (errcode) 489 | { 490 | case EAI_NONAME: 491 | return "Unknown host"; 492 | case EAI_AGAIN: 493 | return "Host name lookup failure"; 494 | /* Errors below are probably WIN32 only */ 495 | #ifdef EAI_BADFLAGS 496 | case EAI_BADFLAGS: 497 | return "Invalid argument"; 498 | #endif 499 | #ifdef EAI_FAMILY 500 | case EAI_FAMILY: 501 | return "Address family not supported"; 502 | #endif 503 | #ifdef EAI_MEMORY 504 | case EAI_MEMORY: 505 | return "Not enough memory"; 506 | #endif 507 | #ifdef EAI_NODATA 508 | #ifndef WIN32_ONLY_COMPILER /* MSVC complains because another case has the 509 | * same value */ 510 | case EAI_NODATA: 511 | return "No host data of that type was found"; 512 | #endif 513 | #endif 514 | #ifdef EAI_SERVICE 515 | case EAI_SERVICE: 516 | return "Class type not found"; 517 | #endif 518 | #ifdef EAI_SOCKTYPE 519 | case EAI_SOCKTYPE: 520 | return "Socket type not supported"; 521 | #endif 522 | default: 523 | return "Unknown server error"; 524 | } 525 | #endif /* HAVE_HSTRERROR */ 526 | } 527 | 528 | /* 529 | * Convert an ipv4 address to a hostname. 530 | * 531 | * Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV 532 | * It will never resolv a hostname. 533 | * - No IPv6 support. 534 | */ 535 | int 536 | getnameinfo(const struct sockaddr * sa, int salen, 537 | char *node, int nodelen, 538 | char *service, int servicelen, int flags) 539 | { 540 | /* Invalid arguments. */ 541 | if (sa == NULL || (node == NULL && service == NULL)) 542 | return EAI_FAIL; 543 | 544 | /* We don't support those. */ 545 | if ((node && !(flags & NI_NUMERICHOST)) 546 | || (service && !(flags & NI_NUMERICSERV))) 547 | return EAI_FAIL; 548 | 549 | #if defined(HAVE_IPV6) || defined(AF_INET6) 550 | if (sa->sa_family == AF_INET6) 551 | return EAI_FAMILY; 552 | #endif 553 | 554 | if (node) 555 | { 556 | int ret = -1; 557 | 558 | if (sa->sa_family == AF_INET) 559 | { 560 | char *p; 561 | 562 | p = inet_ntoa(((struct sockaddr_in *) sa)->sin_addr); 563 | ret = pqt_snprintf(node, nodelen, "%s", p); 564 | } 565 | if (ret == -1) 566 | return EAI_MEMORY; 567 | } 568 | 569 | if (service) 570 | { 571 | int ret = -1; 572 | 573 | if (sa->sa_family == AF_INET) 574 | { 575 | ret = pqt_snprintf(service, servicelen, "%d", 576 | ntohs(((struct sockaddr_in *) sa)->sin_port)); 577 | } 578 | if (ret == -1) 579 | return EAI_MEMORY; 580 | } 581 | 582 | return 0; 583 | } 584 | 585 | #endif /* HAVE_GETADDRINFO */ 586 | 587 | 588 | -------------------------------------------------------------------------------- /source/src/record.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * record.c 4 | * Type handler for the record/composite data type. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | int 15 | pqt_put_record(PGtypeArgs *args) 16 | { 17 | int i; 18 | int len; 19 | char *out; 20 | PGparam *param = va_arg(args->ap, PGparam *); 21 | 22 | PUTNULLCHK(args, param); 23 | 24 | /* watch for invalidation issues */ 25 | if (param->vcnt > args->typhandler->nattrs) 26 | return args->errorf(args, 27 | "param value count is %d but server says it's %d", 28 | param->vcnt, args->typhandler->nattrs); 29 | 30 | /* Auto-fill the remaining fields with SQL NULLs. This feature was 31 | * added because it was needed (by eSilo). We had a few cases where 32 | * we needed to append new fields to existing composites but wanted 33 | * to maintain backwards compatibility ... so some_func(mycomposite) 34 | * would continue to work even for older versions unaware of the 35 | * new composite fields. I guess for some this is unwanted behavior, 36 | * but I think the cases for it are much more common. 37 | */ 38 | if (param->vcnt < args->typhandler->nattrs) 39 | { 40 | int nattrs = args->typhandler->nattrs - param->vcnt; 41 | for (i=0; i < nattrs; i++) 42 | pqt_putparam(param, NULL, 0, 0, BINARYFMT, 43 | args->typhandler->attDescs[param->vcnt].attoid); 44 | } 45 | 46 | /* column count, 4-byte integer */ 47 | len = 4; 48 | 49 | /* determine total byte count to ensure args->put.out is large enough */ 50 | for (i=0; i < param->vcnt; i++) 51 | { 52 | len += (4 + 4); /* oid + len */ 53 | 54 | if (param->vals[i].datal > 0) 55 | len += param->vals[i].datal; 56 | } 57 | 58 | /* ensure out buffer is large enough */ 59 | if (args->put.expandBuffer(args, len) == -1) 60 | return -1; 61 | 62 | out = args->put.out; 63 | 64 | /* write column count */ 65 | pqt_buf_putint4(out, param->vcnt); 66 | out += 4; 67 | 68 | for (i=0; i < param->vcnt; i++) 69 | { 70 | if (param->vals[i].format == 0) 71 | return args->errorf(args, 72 | "Cannot put composite attributes in text format"); 73 | 74 | if (param->vals[i].datal == NULL_LEN) 75 | param->vals[i].oid = args->typhandler->attDescs[i].attoid; 76 | 77 | /* watch for invalidation issues */ 78 | if (param->vals[i].oid != args->typhandler->attDescs[i].attoid) 79 | return args->errorf(args, 80 | "param value OID is %u but server says it's %u", 81 | param->vals[i].oid, args->typhandler->attDescs[i].attoid); 82 | 83 | /* write column oid */ 84 | pqt_buf_putint4(out, param->vals[i].oid); 85 | out += 4; 86 | 87 | /* write column data length */ 88 | pqt_buf_putint4(out, param->vals[i].datal); 89 | out += 4; 90 | 91 | /* write the column data */ 92 | if (param->vals[i].data && param->vals[i].datal > 0) 93 | { 94 | memcpy(out, param->vals[i].data, param->vals[i].datal); 95 | out += param->vals[i].datal; 96 | } 97 | } 98 | 99 | return len; 100 | } 101 | 102 | int 103 | pqt_get_record(PGtypeArgs *args) 104 | { 105 | int i; 106 | int nattrs; 107 | int vlen; 108 | Oid server_oid; 109 | DECLVALUE(args); 110 | PGresult *res; 111 | PGresult **resultp = va_arg(args->ap, PGresult **); 112 | 113 | CHKGETVALS(args, resultp); 114 | 115 | if (args->format == TEXTFMT) 116 | return args->errorf(args, "record does not support text results"); 117 | 118 | /* get record column count, numAttributes */ 119 | nattrs = pqt_buf_getint4(value); 120 | value += 4; 121 | 122 | /* watch for invalidation issues */ 123 | if (args->typhandler->nattrs != nattrs) 124 | return args->errorf(args, 125 | "type handler attribute count is %d but server says it's %d", 126 | args->typhandler->nattrs, nattrs); 127 | 128 | if (!(res = pqt_copyresult(args, nattrs))) 129 | RERR_MEM(args); 130 | 131 | for (i=0; i < nattrs; i++) 132 | { 133 | /* watch for invalidation issues */ 134 | server_oid = (Oid) pqt_buf_getint4(value); 135 | if (server_oid != args->typhandler->attDescs[i].attoid) 136 | { 137 | args->errorf(args, 138 | "type handler attribute OID is %u but server says it's %u", 139 | args->typhandler->attDescs[i].attoid, server_oid); 140 | PQclear(res); 141 | return -1; 142 | } 143 | 144 | /* move past Oid */ 145 | value += 4; 146 | 147 | /* get value length */ 148 | vlen = pqt_buf_getint4(value); 149 | value += 4; 150 | 151 | if (!PQsetvalue(res, 0, i, value, vlen)) 152 | { 153 | PQclear(res); 154 | return -1; 155 | } 156 | 157 | if (vlen > 0) 158 | value += vlen; 159 | } 160 | 161 | *resultp = res; 162 | return 0; 163 | } 164 | 165 | -------------------------------------------------------------------------------- /source/src/spec.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * spec.c 4 | * Type Specifier parser and compiler. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | /* For use with pqt_parse */ 15 | #define CHKSTMTBUF(nbytes_add) do{ \ 16 | if ((*stmtPos + (nbytes_add)) >= stmtBufLen) \ 17 | { \ 18 | PQseterror("statement buffer is too small"); \ 19 | return FALSE; \ 20 | } \ 21 | }while (0) 22 | 23 | /* For use with PQspecPrepare */ 24 | #define FREESTMTBUF do{ \ 25 | if (stmtBuf && stmtBuf != buffer) \ 26 | free(stmtBuf); \ 27 | }while(0) 28 | 29 | static char * 30 | skipQuotes(char *s); 31 | 32 | static char * 33 | parseId(char *id, char **start, int *len, int *flags, int typpos); 34 | 35 | static int 36 | expandSpecs(PGtypeData *typeData); 37 | 38 | int 39 | PQspecPrepare(PGconn *conn, const char *name, 40 | const char *format, int is_stmt) 41 | { 42 | int flags; 43 | int typpos = 0; 44 | int idmax = 0; 45 | size_t stmtPos = 0; 46 | PGtypeHandler *h; 47 | PGtypeData *typeData; 48 | PGtypeSpec *spec; 49 | size_t stmtBufLen = 0; 50 | char *stmtBuf = NULL; 51 | char buffer[8192]; 52 | 53 | if (!conn) 54 | { 55 | PQseterror("PGConn cannot be NULL"); 56 | return FALSE; 57 | } 58 | 59 | if (!name || !*name) 60 | { 61 | PQseterror("Prepared specifier name cannot be NULL or an empty string"); 62 | return FALSE; 63 | } 64 | 65 | if (format && !*format) 66 | { 67 | PQseterror("Specifier format string cannot be empty"); 68 | return FALSE; 69 | } 70 | 71 | if (!isalnum(*name) && *name != '_') 72 | { 73 | PQseterror("Prepared specifier name must begin with an alpha, " 74 | "number or underscore."); 75 | return FALSE; 76 | } 77 | 78 | typeData = PQinstanceData(conn, pqt_eventproc); 79 | if (!typeData) 80 | { 81 | PQseterror("No type data exists for PGconn at %p", conn); 82 | return FALSE; 83 | } 84 | 85 | /* This is a removal request */ 86 | if (!format) 87 | { 88 | int i; 89 | 90 | for (i=0; i < typeData->typspeccnt; i++) 91 | { 92 | if (strcmp(typeData->typspecs[i].name, name) == 0) 93 | { 94 | /* clear it */ 95 | pqt_clearspec(&typeData->typspecs[i]); 96 | 97 | /* remove from list, not needed if its the last element */ 98 | if (i != typeData->typspeccnt - 1) 99 | memmove(typeData->typspecs + i, typeData->typspecs + i + 1, 100 | (typeData->typspeccnt - i - 1) * sizeof(PGtypeSpec)); 101 | 102 | typeData->typspeccnt--; 103 | break; 104 | } 105 | } 106 | 107 | /* always return TRUE, an error is not useful here */ 108 | return TRUE; 109 | } 110 | 111 | /* Already exists case */ 112 | spec = pqt_getspec(typeData->typspecs, typeData->typspeccnt, name); 113 | if (spec) 114 | { 115 | PQseterror("Prepared spec already exists '%s'", name); 116 | return FALSE; 117 | } 118 | 119 | /* Make sure specs array is large enough */ 120 | if (!expandSpecs(typeData)) 121 | return FALSE; 122 | 123 | spec = &typeData->typspecs[typeData->typspeccnt]; 124 | 125 | /* cache statement along with prepared type spec */ 126 | if (is_stmt) 127 | { 128 | stmtBufLen = strlen(format) + 1; 129 | 130 | /* no room in stack, use heap */ 131 | if (stmtBufLen > sizeof(buffer)) 132 | { 133 | stmtBuf = (char *) malloc(stmtBufLen); 134 | if (!stmtBuf) 135 | { 136 | PQseterror(PQT_OUTOFMEMORY); 137 | return FALSE; 138 | } 139 | } 140 | else 141 | { 142 | stmtBuf = buffer; 143 | stmtBufLen = sizeof(buffer); 144 | } 145 | } 146 | 147 | while (format && *format) 148 | { 149 | format = pqt_parse(format, typeData->typhandlers, typeData->typhcnt, 150 | stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags); 151 | 152 | if (!format) 153 | { 154 | pqt_clearspec(spec); 155 | FREESTMTBUF; 156 | return FALSE; 157 | } 158 | 159 | /* skipped harmless chars in format, like quoted sections. */ 160 | if(!h) 161 | continue; 162 | 163 | if (!spec->idlist || spec->idcnt == idmax) 164 | { 165 | int c = idmax ? idmax * 2 : 8; 166 | void *p = pqt_realloc(spec->idlist, c * sizeof(int)); 167 | 168 | if (!p) 169 | { 170 | PQseterror(PQT_OUTOFMEMORY); 171 | pqt_clearspec(spec); 172 | FREESTMTBUF; 173 | return FALSE; 174 | } 175 | 176 | spec->idlist = (int *) p; 177 | 178 | p = pqt_realloc(spec->flags, c * sizeof(char)); 179 | if (!p) 180 | { 181 | PQseterror(PQT_OUTOFMEMORY); 182 | pqt_clearspec(spec); 183 | FREESTMTBUF; 184 | return FALSE; 185 | } 186 | 187 | spec->flags = (unsigned char *) p; 188 | idmax = c; 189 | } 190 | 191 | /* Parallel arrays, every handler needs type flags */ 192 | spec->idlist[spec->idcnt] = h->id; 193 | spec->flags[spec->idcnt++] = (unsigned char) flags; 194 | } 195 | 196 | /* terminate stmtBuf, guarenteed to have room for NUL */ 197 | if (stmtBuf) 198 | stmtBuf[stmtPos] = 0; 199 | 200 | /* copy name string */ 201 | spec->name = strdup(name); 202 | if (!spec->name) 203 | { 204 | pqt_clearspec(spec); 205 | PQseterror(PQT_OUTOFMEMORY); 206 | FREESTMTBUF; 207 | return FALSE; 208 | } 209 | 210 | /* copy the parameterized stmt string */ 211 | if (stmtBuf) 212 | { 213 | spec->stmt = strdup(stmtBuf); 214 | if (!spec->stmt) 215 | { 216 | pqt_clearspec(spec); 217 | PQseterror(PQT_OUTOFMEMORY); 218 | FREESTMTBUF; 219 | return FALSE; 220 | } 221 | } 222 | 223 | FREESTMTBUF; 224 | 225 | /* Success, increment type spec count */ 226 | typeData->typspeccnt++; 227 | return TRUE; 228 | } 229 | 230 | int 231 | PQclearSpecs(PGconn *conn) 232 | { 233 | PGtypeData *typeData; 234 | 235 | if (!conn) 236 | { 237 | PQseterror("PGConn cannot be NULL"); 238 | return FALSE; 239 | } 240 | 241 | typeData = PQinstanceData(conn, pqt_eventproc); 242 | if (!typeData) 243 | { 244 | PQseterror("No type data exists for PGconn at %p", conn); 245 | return FALSE; 246 | } 247 | 248 | pqt_freespecs(typeData->typspecs, typeData->typspeccnt); 249 | typeData->typspecs = NULL; 250 | typeData->typspeccnt = 0; 251 | typeData->typspecmax = 0; 252 | 253 | return TRUE; 254 | } 255 | 256 | char *pqt_parse(const char *format, PGtypeHandler *h, int hcnt, 257 | char *stmtBuf, size_t stmtBufLen, PGtypeHandler **out, size_t *stmtPos, 258 | int *typpos, int *flags) 259 | { 260 | int specMark; 261 | char *s = skipQuotes((char *) format); 262 | char typname[PQT_MAXIDLEN + 1]; 263 | char schema[PQT_MAXIDLEN + 1]; 264 | char tmp[200]; 265 | 266 | *out = NULL; 267 | 268 | if (!s) 269 | return NULL; 270 | 271 | /* found quotes to skip */ 272 | if (s != format) 273 | { 274 | if (stmtBuf) 275 | { 276 | size_t n = s - format; 277 | CHKSTMTBUF(n); 278 | memcpy(stmtBuf + *stmtPos, format, n); 279 | (*stmtPos) += n; 280 | } 281 | 282 | return s; 283 | } 284 | 285 | specMark = *format; 286 | if (specMark != '%' && specMark != '#') 287 | { 288 | if (stmtBuf) 289 | { 290 | CHKSTMTBUF(1); 291 | stmtBuf[*stmtPos] = *format; 292 | (*stmtPos)++; 293 | } 294 | 295 | format++; 296 | return (char *) format; 297 | } 298 | 299 | /* spec skips % or # */ 300 | if (!(s = pqt_parsetype(format + 1, schema, typname, flags, *typpos + 1))) 301 | return NULL; 302 | 303 | if (*flags & TYPFLAG_INVALID) 304 | { 305 | if (stmtBuf) 306 | { 307 | CHKSTMTBUF(1); 308 | stmtBuf[*stmtPos] = *format++; 309 | (*stmtPos)++; 310 | PQseterror(NULL); /* set by pqt_parsetype */ 311 | return (char *) format; 312 | } 313 | 314 | return NULL; 315 | } 316 | 317 | (*typpos)++; 318 | 319 | if (!(*out = pqt_gethandler(h, hcnt, schema, typname))) 320 | { 321 | PQseterror("Uknown type '%s' (position %d)", 322 | pqt_fqtn(tmp, sizeof(tmp), schema, typname), *typpos); 323 | return NULL; 324 | } 325 | 326 | if (stmtBuf) 327 | { 328 | int n = pqt_snprintf(tmp, sizeof(tmp), "$%d", *typpos); 329 | CHKSTMTBUF(n); 330 | memcpy(stmtBuf + *stmtPos, tmp, n); 331 | (*stmtPos) += n; 332 | } 333 | 334 | if (!(*out)->typput) 335 | { 336 | PGtypeHandler *o = pqt_gethandlerbyid(h, hcnt, (*out)->base_id); 337 | if (!o || !o->typput) 338 | { 339 | PQseterror( 340 | "Type '%s' doesn't support put operations (position %d)", 341 | pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema, 342 | (*out)->typname), *typpos); 343 | 344 | *out = NULL; 345 | return NULL; 346 | } 347 | 348 | *out = o; 349 | } 350 | 351 | #if 0 352 | if ((*flags & TYPFLAG_POINTER) && !pqt_allowsptr(*out)) 353 | { 354 | PQseterror( 355 | "Type '%s' doesn't support putting pointers (position %d)", 356 | pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema, 357 | (*out)->typname), *typpos); 358 | 359 | *out = NULL; 360 | return NULL; 361 | } 362 | #endif 363 | 364 | if (specMark == '#') 365 | (*flags) |= TYPFLAG_BYNAME; 366 | 367 | return s; 368 | } 369 | 370 | void 371 | pqt_clearspec(PGtypeSpec *spec) 372 | { 373 | if (spec->name) 374 | free(spec->name); 375 | 376 | if (spec->stmt) 377 | free(spec->stmt); 378 | 379 | if (spec->idlist) 380 | free(spec->idlist); 381 | 382 | if (spec->flags) 383 | free(spec->flags); 384 | 385 | memset(spec, 0, sizeof(PGtypeSpec)); 386 | } 387 | 388 | PGtypeSpec *pqt_dupspecs(PGtypeSpec *specs, int count) 389 | { 390 | int i; 391 | PGtypeSpec *new_specs = (PGtypeSpec *) malloc(count * sizeof(PGtypeSpec)); 392 | 393 | if (!new_specs) 394 | return NULL; 395 | 396 | memset(new_specs, 0, count * sizeof(PGtypeSpec)); 397 | 398 | for (i=0; i < count; i++) 399 | { 400 | PGtypeSpec *s = &specs[i]; 401 | PGtypeSpec *news = &new_specs[i]; 402 | 403 | news->idcnt = s->idcnt; 404 | 405 | news->name = strdup(s->name); 406 | if (!news->name) 407 | { 408 | pqt_freespecs(new_specs, i+1); 409 | return NULL; 410 | } 411 | 412 | if(s->stmt) 413 | { 414 | news->stmt = strdup(s->stmt); 415 | if (!news->stmt) 416 | { 417 | pqt_freespecs(new_specs, i+1); 418 | return NULL; 419 | } 420 | } 421 | 422 | news->idlist = (int *) malloc(s->idcnt * sizeof(int)); 423 | if (!news->idlist) 424 | { 425 | pqt_freespecs(new_specs, i+1); 426 | return NULL; 427 | } 428 | 429 | memcpy(news->idlist, s->idlist, s->idcnt * sizeof(int)); 430 | 431 | news->flags = (unsigned char *) malloc(s->idcnt * sizeof(char)); 432 | if (!news->flags) 433 | { 434 | pqt_freespecs(new_specs, i+1); 435 | return NULL; 436 | } 437 | 438 | memcpy(news->flags, s->flags, s->idcnt * sizeof(char)); 439 | } 440 | 441 | return new_specs; 442 | } 443 | 444 | void pqt_freespecs(PGtypeSpec *specs, int count) 445 | { 446 | int i; 447 | 448 | for (i=0; i < count; i++) 449 | pqt_clearspec(&specs[i]); 450 | 451 | if (specs) 452 | free(specs); 453 | } 454 | 455 | PGtypeSpec *pqt_getspec(PGtypeSpec *specs, int count, const char *name) 456 | { 457 | int i; 458 | 459 | for (i=0; i < count; i++) 460 | if (strcmp(specs[i].name, name) == 0) 461 | return &specs[i]; 462 | 463 | return NULL; 464 | } 465 | 466 | /* Parse a type identifer name (schema qualified or not) from spec. spec 467 | * must point to the first char after the % sign, which maybe a 468 | * double quote. 469 | * 470 | * spec - pointer to typname, just after the '%' or '#' 471 | * schema - buffer to receive schema (PQT_MAXIDLEN bytes) 472 | * typname - buffer to receive typname (PQT_MAXIDLEN bytes) 473 | * flags - a pointer to an int that is set one or more TYPFLAG_xxx 474 | * typpos - 1-based position of spec in specifier string (0 for unknown) 475 | */ 476 | char * 477 | pqt_parsetype(const char *spec, char *schema, char *typname, 478 | int *flags, int typpos) 479 | { 480 | int i; 481 | char *start; 482 | int len=0; 483 | char *s = (char *)spec; 484 | 485 | if (!(s = parseId(s, &start, &len, flags, typpos))) 486 | return NULL; 487 | 488 | /* not a valid specifer, false positive like "(x % y) = 0" */ 489 | if (*flags & TYPFLAG_INVALID) 490 | return s; 491 | 492 | *schema = 0; 493 | if (*s == '.') 494 | { 495 | memcpy(schema, start, len); 496 | schema[len] = 0; 497 | if (*flags & TYPFLAG_CASEFOLD) 498 | for (i=0; i < len; i++) 499 | schema[i] = pqt_tolower(schema[i]); 500 | 501 | /* now get typname */ 502 | if (!(s = parseId(++s, &start, &len, flags, typpos))) 503 | return NULL; 504 | 505 | if (*flags & TYPFLAG_INVALID) 506 | return s; 507 | } 508 | 509 | memcpy(typname, start, len); 510 | typname[len] = 0; 511 | if (*flags & TYPFLAG_CASEFOLD) 512 | for (i=0; i < len; i++) 513 | typname[i] = pqt_tolower(typname[i]); 514 | 515 | return s; 516 | } 517 | 518 | static char * 519 | parseId(char *id, char **start, int *len, int *flags, int typpos) 520 | { 521 | char *p = id; 522 | 523 | *flags = 0; 524 | *start = NULL; 525 | *len = 0; 526 | 527 | if (*p == '"') 528 | p++; 529 | 530 | /* check first character */ 531 | if (!isalpha(*p) && *p != '_') 532 | { 533 | *flags |= TYPFLAG_INVALID; 534 | PQseterror( 535 | "Invalid first character for identifier '%c' (pos:%d)", *p, typpos); 536 | return p; 537 | } 538 | 539 | if (*id == '"') 540 | { 541 | id++; 542 | if (!(p = strchr(id, '"'))) 543 | { 544 | *flags |= TYPFLAG_INVALID; 545 | PQseterror("Unterminated double quote '%s' (pos:%d)", 546 | id-1, typpos); 547 | return p; 548 | } 549 | 550 | *len = (int) (p - id); 551 | *start = id; 552 | p++; 553 | } 554 | else 555 | { 556 | for (p=id+1; isalnum(*p) || *p=='_'; p++) ; 557 | 558 | *len = (int) (p - id); 559 | *start = id; 560 | *flags |= TYPFLAG_CASEFOLD; 561 | } 562 | 563 | /* range check */ 564 | if (*len == 0 || *len > PQT_MAXIDLEN) 565 | { 566 | *flags |= TYPFLAG_INVALID; 567 | PQseterror("Identifier out of range %d (pos:%d), range is 1 to %d", 568 | *len, typpos, PQT_MAXIDLEN); 569 | return p; 570 | } 571 | 572 | /* direct pointer request */ 573 | if (*p == '*') 574 | { 575 | p++; 576 | *flags |= TYPFLAG_POINTER; 577 | } 578 | 579 | /* Is this an array? Ex. %int4[] or %"a b"[] */ 580 | if (p[0] == '[' && p[1] == ']') 581 | { 582 | if (*flags & TYPFLAG_POINTER) 583 | { 584 | PQseterror( 585 | "'*' specifer flag cannot be used with arrays[] '%s' (pos:%d)", 586 | id, typpos); 587 | return NULL; 588 | } 589 | 590 | *flags |= TYPFLAG_ARRAY; 591 | p += 2; 592 | } 593 | 594 | return p; 595 | } 596 | 597 | static int 598 | expandSpecs(PGtypeData *typeData) 599 | { 600 | int n; 601 | PGtypeSpec *specs; 602 | 603 | if (typeData->typspeccnt < typeData->typspecmax) 604 | return TRUE; 605 | 606 | n = typeData->typspecmax ? (typeData->typspecmax * 3) / 2 : 8; 607 | 608 | specs = (PGtypeSpec *) pqt_realloc( 609 | typeData->typspecs, sizeof(PGtypeSpec) * n); 610 | 611 | if (!specs) 612 | { 613 | PQseterror(PQT_OUTOFMEMORY); 614 | return FALSE; 615 | } 616 | 617 | memset(specs + typeData->typspeccnt, 0, 618 | (n - typeData->typspeccnt) * sizeof(PGtypeSpec)); 619 | 620 | typeData->typspecs = specs; 621 | typeData->typspecmax = n; 622 | return TRUE; 623 | } 624 | 625 | /* skip quoted strings. Doesn't need to account for E'' syntax. The 626 | * E is copied over prior to the quoted string. 627 | * 628 | * Returns a pointer to the next character after the closing quote or 629 | * NULL if there was an error. 630 | */ 631 | static char * 632 | skipQuotes(char *s) 633 | { 634 | char *end; 635 | 636 | if (*s != '\'') 637 | return s; 638 | 639 | end = s; 640 | while (*++end) 641 | { 642 | /* If we see a backslash, skip an extra char. No need to dig any 643 | * further since this method works with \digits and \hex. 644 | */ 645 | if (*end == '\\') 646 | end++; 647 | else if (*end == '\'') 648 | break; 649 | } 650 | 651 | /* unterminated quote */ 652 | if (!*end) 653 | { 654 | PQseterror("unterminated single quoted string"); 655 | return NULL; 656 | } 657 | 658 | return ++end; /* skip ending quote */ 659 | } 660 | 661 | -------------------------------------------------------------------------------- /source/src/utils.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * util.c 4 | * Utility functions. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | PGresult * 15 | pqt_copyresult(PGtypeArgs *args, int nattrs) 16 | { 17 | int i; 18 | PGresult *res; 19 | int tableid, columnid, format; 20 | PGresAttDesc *ad = (PGresAttDesc *) malloc(nattrs * sizeof(PGresAttDesc)); 21 | 22 | if (!ad) 23 | { 24 | PQseterror(PQT_OUTOFMEMORY); 25 | return NULL; 26 | } 27 | 28 | tableid = PQftable(args->get.result, args->get.field_num); 29 | columnid = PQftablecol(args->get.result, args->get.field_num); 30 | format = PQfformat(args->get.result, args->get.field_num); 31 | 32 | for (i=0; i < nattrs; i++) 33 | { 34 | ad[i].tableid = tableid; 35 | ad[i].columnid = columnid; 36 | ad[i].format = format; 37 | 38 | /* simple array */ 39 | if (args->typhandler->nattrs == 0) 40 | { 41 | ad[i].typid = args->typhandler->typoid; 42 | ad[i].typlen = args->typhandler->typlen; 43 | ad[i].name = NULL; 44 | ad[i].atttypmod = -1; 45 | } 46 | /* composite/record */ 47 | else 48 | { 49 | ad[i].typid = args->typhandler->attDescs[i].attoid; 50 | ad[i].typlen = args->typhandler->attDescs[i].attlen; 51 | ad[i].name = args->typhandler->attDescs[i].attname; 52 | ad[i].atttypmod = args->typhandler->attDescs[i].atttypmod; 53 | } 54 | } 55 | 56 | res = PQcopyResult(args->get.result, 57 | PG_COPYRES_EVENTS | PG_COPYRES_NOTICEHOOKS); 58 | 59 | if (!res) 60 | { 61 | free(ad); 62 | PQseterror(PQT_OUTOFMEMORY); 63 | return NULL; 64 | } 65 | 66 | if (!PQsetResultAttrs(res, nattrs, ad)) 67 | { 68 | PQclear(res); 69 | PQseterror(PQT_OUTOFMEMORY); 70 | res = NULL; 71 | } 72 | 73 | free(ad); 74 | return res; 75 | } 76 | 77 | #ifdef STRICT_MEMORY_ALIGNMENT 78 | short 79 | pqt_buf_getint2(char *buffer) 80 | { 81 | short n; 82 | memcpy(&n, buffer, 2); 83 | return (short) ntohs(n); 84 | } 85 | 86 | int 87 | pqt_buf_getint4(char *buffer) 88 | { 89 | int n; 90 | memcpy(&n, buffer, 4); 91 | return (int) ntohl(n); 92 | } 93 | #endif 94 | 95 | void 96 | pqt_swap8(void *outp, void *inp, int tonet) 97 | { 98 | static int n = 1; 99 | 100 | #ifdef STRICT_MEMORY_ALIGNMENT 101 | unsigned int in[2]; 102 | unsigned int out[2]; 103 | memcpy(&in, inp, 8); 104 | #else 105 | unsigned int *in = (unsigned int *) inp; 106 | unsigned int *out = (unsigned int *) outp; 107 | #endif 108 | 109 | /* swap when needed */ 110 | if (*(char *)&n == 1) 111 | { 112 | out[0] = (unsigned int) (tonet ? htonl(in[1]) : ntohl(in[1])); 113 | out[1] = (unsigned int) (tonet ? htonl(in[0]) : ntohl(in[0])); 114 | } 115 | else 116 | { 117 | out[0] = in[0]; 118 | out[1] = in[1]; 119 | } 120 | 121 | #ifdef STRICT_MEMORY_ALIGNMENT 122 | memcpy(outp, out, 8); 123 | #endif 124 | } 125 | 126 | int 127 | pqt_text_to_int8(char *val, void *out) 128 | { 129 | PGint8 n; 130 | 131 | /* NOTE: port version of strtoll in port.c. */ 132 | errno = 0; 133 | if ((n = (PGint8) strtoll(val, NULL, 10)) == 0 && errno) 134 | return -1; 135 | 136 | *(PGint8 *) out = n; 137 | return 0; 138 | } 139 | 140 | int 141 | pqt_text_to_float8(double *f8, char *text, char **endptr) 142 | { 143 | double d; 144 | 145 | errno = 0; 146 | if ((d = strtod(text, endptr)) == 0 && errno) 147 | return 0; 148 | 149 | *f8 = d; 150 | return 1; 151 | } 152 | 153 | /* Checks buffer and truncates 'src' if 'dest' is too small. */ 154 | char * 155 | pqt_strcpy(char *dest, size_t size, const char *src) 156 | { 157 | size_t src_len = strlen(src); 158 | 159 | /* truncate if needed */ 160 | if (src_len >= size) 161 | src_len = size - 1; 162 | 163 | memcpy(dest, src, src_len); 164 | dest[src_len] = 0; 165 | return dest; 166 | } 167 | 168 | 169 | /* --------------------------- 170 | * Everything below was taken from postgresql project 171 | * A couple of changes here and there. 172 | */ 173 | 174 | #ifndef HIGHBIT 175 | # define HIGHBIT (0x80) 176 | #endif 177 | 178 | #ifndef IS_HIGHBIT_SET 179 | # define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT) 180 | #endif 181 | 182 | /* 183 | * Fold a character to lower case. 184 | * 185 | * Unlike some versions of tolower(), this is safe to apply to characters 186 | * that aren't upper case letters. Note however that the whole thing is 187 | * a bit bogus for multibyte character sets. 188 | */ 189 | unsigned char 190 | pqt_tolower(unsigned char ch) 191 | { 192 | if (ch >= 'A' && ch <= 'Z') 193 | ch += 'a' - 'A'; 194 | else if (IS_HIGHBIT_SET(ch) && isupper(ch)) 195 | ch = (unsigned char) tolower(ch); 196 | return ch; 197 | } 198 | 199 | /* 200 | * Case-independent comparison of two null-terminated strings. 201 | */ 202 | int 203 | pqt_strcasecmp(const char *s1, const char *s2) 204 | { 205 | for (;;) 206 | { 207 | unsigned char ch1 = (unsigned char) *s1++; 208 | unsigned char ch2 = (unsigned char) *s2++; 209 | 210 | if (ch1 != ch2) 211 | { 212 | if (ch1 >= 'A' && ch1 <= 'Z') 213 | ch1 += 'a' - 'A'; 214 | else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) 215 | ch1 = (unsigned char)tolower(ch1); 216 | 217 | if (ch2 >= 'A' && ch2 <= 'Z') 218 | ch2 += 'a' - 'A'; 219 | else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) 220 | ch2 = (unsigned char)tolower(ch2); 221 | 222 | if (ch1 != ch2) 223 | return (int) ch1 - (int) ch2; 224 | } 225 | if (ch1 == 0) 226 | break; 227 | } 228 | return 0; 229 | } 230 | 231 | /* 232 | * Case-independent comparison of two not-necessarily-null-terminated 233 | * strings. At most n bytes will be examined from each string. 234 | */ 235 | int 236 | pqt_strncasecmp(const char *s1, const char *s2, size_t n) 237 | { 238 | while (n-- > 0) 239 | { 240 | unsigned char ch1 = (unsigned char) *s1++; 241 | unsigned char ch2 = (unsigned char) *s2++; 242 | 243 | if (ch1 != ch2) 244 | { 245 | if (ch1 >= 'A' && ch1 <= 'Z') 246 | ch1 += 'a' - 'A'; 247 | else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) 248 | ch1 = (unsigned char)tolower(ch1); 249 | 250 | if (ch2 >= 'A' && ch2 <= 'Z') 251 | ch2 += 'a' - 'A'; 252 | else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) 253 | ch2 = (unsigned char)tolower(ch2); 254 | 255 | if (ch1 != ch2) 256 | return (int) ch1 - (int) ch2; 257 | } 258 | if (ch1 == 0) 259 | break; 260 | } 261 | return 0; 262 | } 263 | 264 | 265 | 266 | -------------------------------------------------------------------------------- /source/src/varlena.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * varlena.c 4 | * Type handler for the variable length data types. 5 | * 6 | * Copyright (c) 2008-2015 eSilo, LLC. All rights reserved. 7 | * This is free software; see the source for copying conditions. There is 8 | * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 | * PURPOSE. 10 | */ 11 | 12 | #include "libpqtypes-int.h" 13 | 14 | /* put a type in its string representation, text format */ 15 | int 16 | pqt_put_str(PGtypeArgs *args) 17 | { 18 | args->format = TEXTFMT; 19 | args->put.out = va_arg(args->ap, char *); 20 | return args->put.out ? (int)strlen(args->put.out)+1 : 0; 21 | } 22 | 23 | /* varchar, bpchar and name use the text handlers */ 24 | int 25 | pqt_put_text(PGtypeArgs *args) 26 | { 27 | args->put.out = va_arg(args->ap, PGtext); 28 | return args->put.out ? (int)strlen(args->put.out) : 0; 29 | } 30 | 31 | /* varchar, bpchar and name use the text handlers */ 32 | int 33 | pqt_get_text(PGtypeArgs *args) 34 | { 35 | DECLVALUE(args); 36 | PGtext *textp = va_arg(args->ap, PGtext *); 37 | CHKGETVALS(args, textp); 38 | *textp = value; 39 | return 0; 40 | } 41 | 42 | int 43 | pqt_put_bytea(PGtypeArgs *args) 44 | { 45 | PGbytea *bytea = va_arg(args->ap, PGbytea *); 46 | PUTNULLCHK(args, bytea); 47 | args->put.out = bytea->data; 48 | return bytea->len; 49 | } 50 | 51 | int 52 | pqt_get_bytea(PGtypeArgs *args) 53 | { 54 | DECLVALUE(args); 55 | DECLLENGTH(args); 56 | PGbytea *bytea = va_arg(args->ap, PGbytea *); 57 | 58 | CHKGETVALS(args, bytea); 59 | 60 | if (args->format == TEXTFMT) 61 | { 62 | size_t len = 0; 63 | 64 | value = (char *) PQunescapeBytea((const unsigned char *) value, &len); 65 | if (!value) 66 | RERR_STR2INT(args); 67 | 68 | bytea->data = (char *) PQresultAlloc(args->get.result, len); 69 | if (!bytea->data) 70 | { 71 | PQfreemem(value); 72 | RERR_MEM(args); 73 | } 74 | 75 | bytea->len = (int) len; 76 | memcpy(bytea->data, value, len); 77 | PQfreemem(value); 78 | return 0; 79 | } 80 | 81 | /* binary format */ 82 | bytea->len = valuel; 83 | bytea->data = value; 84 | return 0; 85 | } 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /source/win32.mak: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # Project: libpqtypes 3 | # Makefile for Microsoft Visual Studio 6, 7 or 8 4 | # 5 | # nmake -f win32.mak [options] [targets] 6 | # 7 | # For further build instructions, see the package's INSTALL file. 8 | # 9 | # Authors: Andrew Chernow, Merlin Moncure 10 | # Contact: libpqtypes@esilo.com 11 | ############################################################## 12 | 13 | PROJNAME = libpqtypes 14 | 15 | OBJECTS = src\array.obj src\datetime.obj src\error.obj \ 16 | src\events.obj src\exec.obj src\geo.obj src\handler.obj \ 17 | src\misc.obj src\network.obj src\numerics.obj \ 18 | src\param.obj src\port.obj src\record.obj src\spec.obj \ 19 | src\utils.obj src\varlena.obj 20 | 21 | 22 | CFLAGS = $(CFLAGS) /nologo /W4 /MD /GF /Ob2 /O2 /Oi- /D_WIN32_WINNT=0x0501 23 | 24 | !IFDEF MT 25 | CFLAGS = $(CFLAGS) /DPQT_THREAD_SAFE 26 | !ENDIF 27 | 28 | !IFDEF PQT_LONG_LONG 29 | CFLAGS = $(CFLAGS) /DPQT_LONG_LONG=$(PQT_LONG_LONG) 30 | !ENDIF 31 | 32 | # set the libpath for libpq.dll and libpqdll.lib (same thing as gcc -L) 33 | !IFDEF LPATH 34 | LIBPATH = /LIBPATH:$(LPATH) 35 | !ENDIF 36 | 37 | INC2 = $(INC) /Isrc 38 | LIBS = ws2_32.lib libpq.lib 39 | LINKOPTS = /nologo /subsystem:windows /incremental:no 40 | LINKIGNORE = /IGNORE:4089 /IGNORE:4006 /IGNORE:4068 41 | LIBOPTS = /nologo /subsystem:windows $(LINKIGNORE) 42 | 43 | all: $(OBJECTS) 44 | lib $(LIBOPTS) /OUT:$(PROJNAME).lib $(OBJECTS) 45 | link $(LINKOPTS) /dll $(OBJECTS) /out:$(PROJNAME).dll \ 46 | /IMPLIB:$(PROJNAME)dll.lib $(LIBS) $(LIBPATH) 47 | 48 | test: 49 | $(CC) $(CFLAGS) $(INC2) src\regression-test.c /Feregtest.exe \ 50 | $(LIBS) $(PROJNAME)dll.lib /link $(LIBPATH) $(LINKIGNORE) 51 | -@del regression-test.obj 52 | 53 | $(OBJECTS): 54 | $(CC) $(CFLAGS) $(INC2) /c $** /Fo$*.obj 55 | 56 | clean: 57 | -@del /Q /F \ 58 | $(OBJECTS) \ 59 | $(PROJNAME).dll \ 60 | $(PROJNAME).dll.manifest \ 61 | $(PROJNAME).lib \ 62 | $(PROJNAME)dll.lib \ 63 | $(PROJNAME)dll.exp \ 64 | regtest.exe \ 65 | regtest.exe.manifest 2>NUL 1>NUL 66 | 67 | -------------------------------------------------------------------------------- /source/win32_d.mak: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # Project: libpqtypes 3 | # Makefile for Microsoft Visual Studio 6, 7 or 8 4 | # 5 | # nmake -f win32.mak [options] [targets] 6 | # 7 | # For further build instructions, see the package's INSTALL file. 8 | # 9 | # Authors: Andrew Chernow, Merlin Moncure 10 | # Contact: libpqtypes@esilo.com 11 | ############################################################## 12 | 13 | PROJNAME = libpqtypes 14 | 15 | OBJECTS = src\array.obj src\datetime.obj src\error.obj \ 16 | src\events.obj src\exec.obj src\geo.obj src\handler.obj \ 17 | src\misc.obj src\network.obj src\numerics.obj \ 18 | src\param.obj src\port.obj src\record.obj src\spec.obj \ 19 | src\utils.obj src\varlena.obj 20 | 21 | 22 | CFLAGS = $(CFLAGS) /nologo /W3 /Zi /WX- /Od /MD /GF /Ob2 /Oi- /D_WIN32_WINNT=0x0501 /D "_DEBUG" 23 | 24 | !IFDEF MT 25 | CFLAGS = $(CFLAGS) /DPQT_THREAD_SAFE 26 | !ENDIF 27 | 28 | !IFDEF PQT_LONG_LONG 29 | CFLAGS = $(CFLAGS) /DPQT_LONG_LONG=$(PQT_LONG_LONG) 30 | !ENDIF 31 | 32 | # set the libpath for libpq.dll and libpqdll.lib (same thing as gcc -L) 33 | !IFDEF LPATH 34 | LIBPATH = /LIBPATH:$(LPATH) 35 | !ENDIF 36 | 37 | INC2 = $(INC) /Isrc 38 | LIBS = ws2_32.lib libpq.lib 39 | LINKOPTS = /nologo /subsystem:windows /incremental /DEBUG 40 | LINKIGNORE = /IGNORE:4089 /IGNORE:4006 /IGNORE:4068 41 | LIBOPTS = /nologo /subsystem:windows $(LINKIGNORE) 42 | 43 | all: $(OBJECTS) 44 | lib $(LIBOPTS) /OUT:$(PROJNAME).lib $(OBJECTS) 45 | link $(LINKOPTS) /dll $(OBJECTS) /out:$(PROJNAME).dll \ 46 | /IMPLIB:$(PROJNAME)dll.lib $(LIBS) $(LIBPATH) 47 | 48 | test: 49 | $(CC) $(CFLAGS) $(INC2) src\regression-test.c /Feregtest.exe \ 50 | $(LIBS) $(PROJNAME)dll.lib /link $(LIBPATH) $(LINKIGNORE) 51 | -@del regression-test.obj 52 | 53 | $(OBJECTS): 54 | $(CC) $(CFLAGS) $(INC2) /c $** /Fo$*.obj 55 | 56 | clean: 57 | -@del /Q /F \ 58 | $(OBJECTS) \ 59 | $(PROJNAME).dll \ 60 | $(PROJNAME).dll.manifest \ 61 | $(PROJNAME).lib \ 62 | $(PROJNAME)dll.lib \ 63 | $(PROJNAME)dll.exp \ 64 | regtest.exe \ 65 | regtest.exe.manifest 2>NUL 1>NUL 66 | 67 | --------------------------------------------------------------------------------