mirror of https://github.com/postgres/postgres
parent
2a67742412
commit
6e66468f3a
@ -0,0 +1,44 @@ |
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
#============ Default for all system ==============
|
||||
OS = UNX
|
||||
SHELL = /bin/sh
|
||||
AR = ar r
|
||||
DLSUFFIX= so
|
||||
INCDIR = /include
|
||||
OBJX =
|
||||
RANLIB = ranlib
|
||||
INSTALL = /usr/bin/install
|
||||
INSTALL_DATA = $(INSTALL) -c -m 644
|
||||
MKDIR = mkdir
|
||||
DESTDIR = /usr/local
|
||||
LIBDIR = /lib
|
||||
INSTHEADERS = isql.h isqlext.h iodbc.h
|
||||
DESTLIBDIR = $(DESTDIR)$(LIBDIR)
|
||||
DESTINCDIR = $(DESTDIR)$(INCDIR)/iodbc
|
||||
ODBCSYSTEMDIR = /usr/home/gryschuk
|
||||
|
||||
# Remove the comment characters from the section you want to
|
||||
# use below, make sure all other sections are commented out.
|
||||
|
||||
#============== Linux ELF =========================
|
||||
CC = gcc
|
||||
PIC = -fPIC
|
||||
CFLAGSX = -g
|
||||
#CFLAGSX = -g -Wall -DMY_LOG -DQ_LOG
|
||||
LDFLAGS = -shared
|
||||
LIBS = -ldl
|
||||
|
||||
#============= FreeBSD 2.x ========================
|
||||
# I don't know if this would work but you can always just try it.
|
||||
# PIC = -fPIC
|
||||
# CFLAGSX = -g -Wall
|
||||
# LDFLAGS = -Bshareable
|
||||
# LIBS =
|
||||
|
||||
#===| end of file 'Config.mk' |===
|
||||
|
||||
|
||||
@ -0,0 +1,85 @@ |
||||
|
||||
# .include "Version.mk"
|
||||
# .include "Config.mk"
|
||||
include Version.mk |
||||
include Config.mk |
||||
#==============================================================================
|
||||
# Makefile
|
||||
#
|
||||
# UNIX Makefile to build the CLI for PostgreSQL/Postgres95
|
||||
#
|
||||
#==============================================================================
|
||||
# Site specific configuration (UNIX)
|
||||
#==============================================================================
|
||||
#
|
||||
# option switches
|
||||
#
|
||||
# debug: select this to enable debugging of the software
|
||||
#DEBUG = -D_DEBUG
|
||||
#
|
||||
#==============================================================================
|
||||
|
||||
#---| definitions |------------------------------------------------------------
|
||||
|
||||
# NAME = cli
|
||||
NAME = psqlodbc
|
||||
|
||||
OBJECTS = info.o bind.o columninfo.o connection.o convert.o drvconn.o \
|
||||
environ.o execute.o lobj.o misc.o options.o \
|
||||
pgtypes.o psqlodbc.o qresult.o results.o socket.o statement.o \
|
||||
gpps.o tuple.o tuplelist.o dlg_specific.o $(OBJX)
|
||||
|
||||
#CFLAGS = -c $(DEBUG) -D$(PG_ENV) -O $(PIC) $(ANSI) -I$(PG_INCLUDE) \
|
||||
# -I$(ODBC_INCLUDE) -D$(DLDAPI) $(CFLAGSX) -DHAVE_CONFIG_H \
|
||||
# -DVERSION=\"$(VERSION)$(EXTVER)\"
|
||||
CFLAGS = -g -c -Wall $(DEBUG) -O $(PIC) $(ANSI) -I. -I.. \
|
||||
-I$(PG_INCLUDE) -I$(ODBC_INCLUDE) $(CFLAGSX) -DHAVE_CONFIG_H
|
||||
|
||||
shlib = lib$(NAME)-$(VERSION).$(DLSUFFIX)
|
||||
DESTDIR = /usr/local
|
||||
LIBDIR = /lib
|
||||
|
||||
#---| global dependencies |----------------------------------------------------
|
||||
|
||||
#all: $(OBJECTS) lib dll
|
||||
|
||||
all: $(OBJECTS) dll |
||||
|
||||
clean: |
||||
-rm -f core *.o *~ *.core
|
||||
|
||||
delete: clean |
||||
|
||||
delete_all: delete |
||||
-rm -f /usr/local/lib/lib$(NAME)_$(MSQL_ENV).a
|
||||
-rm -f /usr/local/lib/lib$(NAME)_$(MSQL_ENV).$(DLSUFFIX)
|
||||
|
||||
#---| local dependencies |-----------------------------------------------------
|
||||
#log.o: ../SRC_LOG/log.c ../SRC_LOG/log.h
|
||||
# $(CC) $(CFLAGS) -I../SRC_LOG ../SRC_LOG/log.c
|
||||
|
||||
lib: |
||||
$(AR) lib$(NAME)$(PG_ENV).a $(OBJECTS)
|
||||
$(RANLIB) lib$(NAME)$(PG_ENV).a
|
||||
|
||||
dll: $(OBJECTS) |
||||
$(LD) $(LDFLAGS) -L$(PG_LIBPATH) $(OBJECTS) \
|
||||
-o $(shlib) $(LIBS) $(PG_LIBS)
|
||||
|
||||
install-shlib: $(shlib) |
||||
$(INSTALL_DATA) $(shlib) $(DESTDIR)$(LIBDIR)/$(shlib)
|
||||
rm -f $(DESTDIR)$(LIBDIR)/lib$(NAME).so
|
||||
ln -sf $(shlib) $(DESTDIR)$(LIBDIR)/lib$(NAME).so
|
||||
|
||||
install-headers: $(INSTHEADERS) |
||||
if [ -d $(DESTDIR)$(INCDIR)/iodbc ]; then : ; else $(MKDIR) $(DESTDIR)$(INCDIR)/iodbc; fi
|
||||
$(INSTALL_DATA) $(INSTHEADERS) $(DESTDIR)$(INCDIR)/iodbc
|
||||
|
||||
install-ini: odbcinst.ini |
||||
$(INSTALL_DATA) odbcinst.ini /etc
|
||||
|
||||
install: install-headers install-shlib install-ini |
||||
|
||||
#==============================================================================
|
||||
|
||||
|
||||
@ -0,0 +1,113 @@ |
||||
|
||||
Unix port of psqlodbc, brought to you by: |
||||
Gerald Gryschuk(ggryschuk@home.com) |
||||
|
||||
This is the first release of a port of psqlodbc to Unix(specifically Linux), |
||||
as such the installation may not be as straight forward as it could be(then |
||||
again it might be). As well the only testing of the driver has been with |
||||
the real project I'm working on, since it seems to be working there I assumed |
||||
it was ready to go out. This port works with Ke Jin's iodbc driver manager |
||||
although there is no theoretical reason why it couldn't work with other |
||||
driver managers for UNIX(I know of none though). The FreeODBC site |
||||
(http://users.ids.net/~bjepson/freeODBC/) has a link to download the current |
||||
version of iodbc(iodbc-2.12). |
||||
|
||||
This driver has been successfully compiled and tested on a RedHat 4.1 system |
||||
using both gcc 2.7.2 and egcs 1.0.2. |
||||
|
||||
INSTALLATION: |
||||
|
||||
If you have a system any where close to mine this will be easy, just |
||||
copy Makefile.lnx to Makefile then type 'make' and see what happens. Note |
||||
that if you have not enabled logging(read the file misc.h) then you |
||||
may get alot of warning messages, just ignore these, they shouldn't be |
||||
a problem. If this doesn't work, well... I don't know what to say, see if |
||||
you can figure out what's wrong,fix it and send me a message. If everything |
||||
makes o.k. you can 'make install' which will install the shared library |
||||
(in /usr/local/lib) and a WINDOWS type INI file in /etc (called odbcinst.ini, |
||||
see CONFIGURATION below). If you want to program using this driver do a |
||||
'make install-headers' which will install programming header files in |
||||
/usr/local/include/iodbc. If you don't like these install locations edit |
||||
Config.mk and change the environment variable DESTDIR(and possible DESTINCDIR |
||||
to get rid of the /iodbc) to suit your system. |
||||
|
||||
CONFIGURATION: |
||||
|
||||
The psqlodbc driver reads two Windows type INI files for configuration, |
||||
one called odbcinst.ini located in /etc which is used for system wide |
||||
configuration of the driver and one in the users home directory called |
||||
.odbc.ini, which can be used to override the system wide settings. Note that |
||||
the location of odbcinst is currently hardcoded into the source so if you |
||||
want to change this you have to change it in the source as well. This latter |
||||
file is also searched for by iodbc and is where |
||||
the DataSource definitions are placed. The format of both files is exactly |
||||
like that of a Windows INI file using key,value pairs. A DataSource definition |
||||
is started with a section name enclosed in square brackets i.e. [PostODBC]. |
||||
Comments are started using a ';' character and are restricted to being |
||||
only on seperate lines(i.e. no comments are allowed on definition lines). |
||||
The important keywords for psqlodbc are: |
||||
Driver = (location where the psqlodbc library was installed) |
||||
ex. Driver = /usr/local/lib/libpspqlodbc.so |
||||
|
||||
ServerName = hostname or ip-address of postgreSQL server |
||||
ex. ServerName = simpsons.springfield.com |
||||
or ServerName = 192.1.1.1 |
||||
|
||||
Database = name of database to connect to |
||||
ex. Database = template1 |
||||
|
||||
Port = tcp port that the postgreSQL server is listening on, note |
||||
that this need not be set as the driver defaults to the |
||||
default postgreSQL port of 5432. Of course if your server |
||||
is listening on a different port than you need to say |
||||
what it is here. |
||||
|
||||
Username = name of authorized postgreSQL user |
||||
ex. Username = homer |
||||
|
||||
Password = the password for the user named in Username. Note |
||||
that if you have password checking on in postgreSQL |
||||
you have to have this field. Unfortunately this means |
||||
storing clear text passwords in a file. If this bothers |
||||
you, well... write a dialog box routine and send it |
||||
to me. |
||||
ex. Password = Doh! |
||||
|
||||
ReadOnly = 0 or 1. Default is 1 => database IS readonly. If |
||||
set to 0, database is read-write. |
||||
ex. ReadOnly = 0 |
||||
|
||||
Protocol = 6.2 to force the use of Postgres 6.2 protocol |
||||
|
||||
The odbcinst.ini file is where sytem wide settings are kept. There are |
||||
quite a number of these, all of which have built-in defaults. Since I'm |
||||
not even sure what they are all for I won't try to describe them, check |
||||
the file dlg_specific.h for a list and some explanation. Two that I found |
||||
useful are Debug and CommLog, which can be used to tailor logging of messages |
||||
by the driver(note that you have to have defined MY_LOG and Q_LOG during |
||||
compilation of the driver, see the file misc.h for an explanation). If |
||||
you have logging On(ie. CommLog = 1 and/or Debug = 1) then logging will |
||||
occur to the files /tmp/mylog.log(Debug, only developers will be |
||||
interested) and /tmp/psqlodbc.log(end user log file). |
||||
|
||||
USE: |
||||
run an ODBC enabled application :-) . |
||||
|
||||
O.k. Well, the only ODBC compliant applications that I can |
||||
"guarantee" to work are those that are compiled using the following |
||||
headers that come with this driver, iodbc.h, isql.h and isqlext.h. |
||||
|
||||
BUGS,etc. |
||||
|
||||
If you have problems with compiling/installing or making this |
||||
package send e-mail to me at: |
||||
gerald.gryschuk@home.com or to the pgsql-interfaces mailing list |
||||
pgsql-interfaces@postgresql.org . |
||||
|
||||
Ports to different Unices are greatly appreciated and can probably be |
||||
sent to me for now(although this may change shortly). |
||||
|
||||
Bugs of a general nature should still be sent to the current |
||||
maintainer or to the interfaces list. |
||||
|
||||
|
||||
@ -0,0 +1,3 @@ |
||||
VERSION = 0.24
|
||||
EXTVER = .2
|
||||
|
||||
@ -0,0 +1,5 @@ |
||||
#define HAVE_IODBC 1 |
||||
#define UNIX 1 |
||||
#define HAVE_PARAM_H 1 |
||||
#define HAVE_PWD_H 1 |
||||
#define HAVE_STRICMP 0 |
||||
@ -0,0 +1,475 @@ |
||||
// GetPrivateProfileString() -- approximate implementation of
|
||||
// Windows NT System Services version of GetPrivateProfileString()
|
||||
// probably doesn't handle the NULL key for section name or value key
|
||||
// correctly also, doesn't provide Microsoft backwards compatability
|
||||
// wrt TAB characters in the value string -- Microsoft terminates value
|
||||
// at the first TAB, but I couldn't discover what the behavior should
|
||||
// be regarding TABS in quoted strings so, I treat tabs like any other
|
||||
// characters -- NO comments following value string separated by a TAB
|
||||
// are allowed (that is an anachronism anyway)
|
||||
// Added code to search for ODBC_INI file in users home directory on
|
||||
// Unix
|
||||
|
||||
#ifndef WIN32 |
||||
|
||||
#if HAVE_CONFIG_H |
||||
#include <config.h> // produced by configure |
||||
#endif |
||||
|
||||
#include <stdio.h> |
||||
#include <unistd.h> |
||||
|
||||
#if HAVE_PWD_H |
||||
#include <pwd.h> |
||||
#endif |
||||
|
||||
#include <sys/types.h> |
||||
#include <string.h> |
||||
#include "gpps.h" |
||||
|
||||
#ifndef TRUE |
||||
#define TRUE ((BOOL)1) |
||||
#endif |
||||
#ifndef FALSE |
||||
#define FALSE ((BOOL)0) |
||||
#endif |
||||
|
||||
#if HAVE_PARAM_H |
||||
#include <sys/param.h> |
||||
#else |
||||
#define MAXPATHLEN 255 |
||||
#endif |
||||
|
||||
DWORD |
||||
GetPrivateProfileString(char *theSection, // section name
|
||||
char *theKey, // search key name
|
||||
char *theDefault, // default value if not found
|
||||
char *theReturnBuffer, // return value stored here
|
||||
size_t theReturnBufferLength, // byte length of return buffer
|
||||
char *theIniFileName) // pathname of ini file to search
|
||||
{ |
||||
char buf[MAXPATHLEN+1]; |
||||
char* ptr = 0; |
||||
FILE* aFile = 0; |
||||
size_t aLength; |
||||
char aLine[2048]; |
||||
char *aValue; |
||||
char *aString; |
||||
size_t aLineLength; |
||||
size_t aReturnLength = 0; |
||||
|
||||
BOOL aSectionFound = FALSE; |
||||
BOOL aKeyFound = FALSE; |
||||
int j = 0; |
||||
|
||||
j = strlen(theIniFileName) + 1; |
||||
ptr = (char*)getpwuid(getuid()); // get user info
|
||||
|
||||
if( ptr == NULL) |
||||
{ |
||||
if( MAXPATHLEN < j ) |
||||
theIniFileName[MAXPATHLEN] = '\0'; |
||||
|
||||
sprintf(buf,"%s",theIniFileName); |
||||
} |
||||
ptr = ((struct passwd*)ptr)->pw_dir; // get user home dir
|
||||
if( ptr == NULL || *ptr == '\0' ) |
||||
ptr = "/home"; |
||||
|
||||
/* This doesn't make it so we find an ini file but allows normal
|
||||
* processing to continue further on down. The likelihood is that |
||||
* the file won't be found and thus the default value will be |
||||
* returned. |
||||
*/ |
||||
if( MAXPATHLEN < strlen(ptr) + j ) |
||||
{ |
||||
if( MAXPATHLEN < strlen(ptr) ) |
||||
ptr[MAXPATHLEN] = '\0'; |
||||
else |
||||
theIniFileName[MAXPATHLEN-strlen(ptr)] = '\0'; |
||||
} |
||||
|
||||
sprintf( buf, "%s/%s",ptr,theIniFileName ); |
||||
|
||||
/* This code makes it so that a file in the users home dir
|
||||
* overrides a the "default" file as passed in |
||||
*/ |
||||
aFile = (FILE*)(buf ? fopen(buf, "r") : NULL); |
||||
if(!aFile) { |
||||
sprintf(buf,"%s",theIniFileName); |
||||
aFile = (FILE*)(buf ? fopen(buf, "r") : NULL); |
||||
} |
||||
|
||||
|
||||
aLength = (theDefault == NULL) ? 0 : strlen(theDefault); |
||||
|
||||
if(theReturnBufferLength == 0 || theReturnBuffer == NULL) |
||||
{ |
||||
if(aFile) |
||||
{ |
||||
fclose(aFile); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
if(aFile == NULL) |
||||
{ |
||||
// no ini file specified, return the default
|
||||
|
||||
++aLength; // room for NULL char
|
||||
aLength = theReturnBufferLength < aLength ? |
||||
theReturnBufferLength : aLength; |
||||
strncpy(theReturnBuffer, theDefault, aLength); |
||||
theReturnBuffer[aLength - 1] = '\0'; |
||||
return aLength - 1; |
||||
} |
||||
|
||||
|
||||
while(fgets(aLine, sizeof(aLine), aFile) != NULL) |
||||
{ |
||||
aLineLength = strlen(aLine); |
||||
// strip final '\n'
|
||||
if(aLineLength > 0 && aLine[aLineLength - 1] == '\n') |
||||
{ |
||||
aLine[aLineLength - 1] = '\0'; |
||||
} |
||||
switch(*aLine) |
||||
{ |
||||
case ' ': // blank line
|
||||
case ';': // comment line
|
||||
continue; |
||||
break; |
||||
|
||||
case '[': // section marker
|
||||
|
||||
if( (aString = strchr(aLine, ']')) ) |
||||
{ |
||||
*aString = '\0'; |
||||
|
||||
// accept as matched if NULL key or exact match
|
||||
|
||||
if(!theSection || !strcmp(aLine + 1, theSection)) |
||||
{ |
||||
aSectionFound = TRUE; |
||||
} |
||||
} |
||||
|
||||
break; |
||||
|
||||
default: |
||||
|
||||
// try to match value keys if in proper section
|
||||
|
||||
if(aSectionFound) |
||||
{ |
||||
// try to match requested key
|
||||
|
||||
if( (aString = aValue = strchr(aLine, '=')) ) |
||||
{ |
||||
*aValue = '\0'; |
||||
++aValue; |
||||
|
||||
// strip leading blanks in value field
|
||||
|
||||
while(*aValue == ' ' && aValue < aLine + sizeof(aLine)) |
||||
{ |
||||
*aValue++ = '\0'; |
||||
} |
||||
if(aValue >= aLine + sizeof(aLine)) |
||||
{ |
||||
aValue = ""; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
aValue = ""; |
||||
} |
||||
|
||||
// strip trailing blanks from key
|
||||
|
||||
if(aString) |
||||
{ |
||||
while(--aString >= aLine && *aString == ' ') |
||||
{ |
||||
*aString = '\0'; |
||||
} |
||||
} |
||||
|
||||
// see if key is matched
|
||||
|
||||
if(theKey == NULL || !strcmp(theKey, aLine)) |
||||
{ |
||||
// matched -- first, terminate value part
|
||||
|
||||
aKeyFound = TRUE; |
||||
aLength = strlen(aValue); |
||||
|
||||
// remove trailing blanks from aValue if any
|
||||
|
||||
aString = aValue + aLength - 1; |
||||
|
||||
while(--aString > aValue && *aString == ' ') |
||||
{ |
||||
*aString = '\0'; |
||||
--aLength; |
||||
} |
||||
|
||||
// unquote value if quoted
|
||||
|
||||
if(aLength >= 2 && aValue[0] == '"' && |
||||
aValue[aLength - 1] == '"') |
||||
{ |
||||
// string quoted with double quotes
|
||||
|
||||
aValue[aLength - 1] = '\0'; |
||||
++aValue; |
||||
aLength -= 2; |
||||
} |
||||
else |
||||
{ |
||||
// single quotes allowed also...
|
||||
|
||||
if(aLength >= 2 && aValue[0] == '\'' && |
||||
aValue[aLength - 1] == '\'') |
||||
{ |
||||
aValue[aLength - 1] = '\0'; |
||||
++aValue; |
||||
aLength -= 2; |
||||
} |
||||
} |
||||
|
||||
// compute maximum length copyable
|
||||
|
||||
aLineLength = (aLength < |
||||
theReturnBufferLength - aReturnLength) ? aLength : |
||||
theReturnBufferLength - aReturnLength; |
||||
|
||||
// do the copy to return buffer
|
||||
|
||||
if(aLineLength) |
||||
{ |
||||
strncpy(&theReturnBuffer[aReturnLength], |
||||
aValue, aLineLength); |
||||
aReturnLength += aLineLength; |
||||
if(aReturnLength < theReturnBufferLength) |
||||
{ |
||||
theReturnBuffer[aReturnLength] = '\0'; |
||||
++aReturnLength; |
||||
} |
||||
} |
||||
if(aFile) |
||||
{ |
||||
fclose(aFile); |
||||
aFile = NULL; |
||||
} |
||||
|
||||
return aReturnLength > 0 ? aReturnLength - 1 : 0; |
||||
} |
||||
} |
||||
|
||||
break; |
||||
} |
||||
} |
||||
|
||||
if(aFile) |
||||
{ |
||||
fclose(aFile); |
||||
} |
||||
|
||||
if(!aKeyFound) { // key wasn't found return default
|
||||
++aLength; // room for NULL char
|
||||
aLength = theReturnBufferLength < aLength ? |
||||
theReturnBufferLength : aLength; |
||||
strncpy(theReturnBuffer, theDefault, aLength); |
||||
theReturnBuffer[aLength - 1] = '\0'; |
||||
aReturnLength = aLength - 1; |
||||
} |
||||
return aReturnLength > 0 ? aReturnLength - 1 : 0; |
||||
} |
||||
|
||||
DWORD |
||||
WritePrivateProfileString(char *theSection, // section name
|
||||
char *theKey, // write key name
|
||||
char *theBuffer, // input buffer
|
||||
char *theIniFileName) // pathname of ini file to write
|
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/* Ok. What the hell's the default behaviour for a null input buffer, and null
|
||||
* section name. For now if either are null I ignore the request, until |
||||
* I find out different. |
||||
DWORD |
||||
WritePrivateProfileString(char *theSection, // section name
|
||||
char *theKey, // write key name
|
||||
char *theBuffer, // input buffer
|
||||
char *theIniFileName) // pathname of ini file to write
|
||||
{ |
||||
char buf[MAXPATHLEN+1]; |
||||
char* ptr = 0; |
||||
FILE* aFile = 0; |
||||
size_t aLength; |
||||
char aLine[2048]; |
||||
char *aValue; |
||||
char *aString; |
||||
size_t aLineLength; |
||||
size_t aReturnLength = 0; |
||||
|
||||
BOOL aSectionFound = FALSE; |
||||
BOOL keyFound = FALSE; |
||||
int j = 0; |
||||
|
||||
// If this isn't correct processing we'll change it later
|
||||
if(theSection == NULL || theKey == NULL || theBuffer == NULL ||
|
||||
theIniFileName == NULL) return 0; |
||||
|
||||
aLength = strlen(theBuffer); |
||||
if(aLength == 0) return 0; |
||||
|
||||
j = strlen(theIniFileName) + 1; |
||||
ptr = (char*)getpwuid(getuid()); // get user info
|
||||
|
||||
if( ptr == NULL) |
||||
{ |
||||
if( MAXPATHLEN < j ) |
||||
theIniFileName[MAXPATHLEN] = '\0'; |
||||
|
||||
sprintf(buf,"%s",theIniFileName); |
||||
} |
||||
ptr = ((struct passwd*)ptr)->pw_dir; // get user home dir
|
||||
if( ptr == NULL || *ptr == '\0' ) |
||||
ptr = "/home"; |
||||
|
||||
// This doesn't make it so we find an ini file but allows normal
|
||||
// processing to continue further on down. The likelihood is that
|
||||
// the file won't be found and thus the default value will be
|
||||
// returned.
|
||||
//
|
||||
if( MAXPATHLEN < strlen(ptr) + j ) |
||||
{ |
||||
if( MAXPATHLEN < strlen(ptr) ) |
||||
ptr[MAXPATHLEN] = '\0'; |
||||
else |
||||
theIniFileName[MAXPATHLEN-strlen(ptr)] = '\0'; |
||||
} |
||||
|
||||
sprintf( buf, "%s/%s",ptr,theIniFileName ); |
||||
|
||||
// This code makes it so that a file in the users home dir
|
||||
// overrides a the "default" file as passed in
|
||||
//
|
||||
aFile = (FILE*)(buf ? fopen(buf, "r+") : NULL); |
||||
if(!aFile) { |
||||
sprintf(buf,"%s",theIniFileName); |
||||
aFile = (FILE*)(buf ? fopen(buf, "r+") : NULL); |
||||
if(!aFile) return 0; |
||||
} |
||||
|
||||
|
||||
aLength = strlen(theBuffer); |
||||
|
||||
// We have to search for theKey, because if it already
|
||||
// exists we have to overwrite it. If it doesn't exist
|
||||
// we just write a new line to the file.
|
||||
//
|
||||
while(fgets(aLine, sizeof(aLine), aFile) != NULL) |
||||
{ |
||||
aLineLength = strlen(aLine); |
||||
// strip final '\n'
|
||||
if(aLineLength > 0 && aLine[aLineLength - 1] == '\n') |
||||
{ |
||||
aLine[aLineLength - 1] = '\0'; |
||||
} |
||||
switch(*aLine) |
||||
{ |
||||
case ' ': // blank line
|
||||
case ';': // comment line
|
||||
continue; |
||||
break; |
||||
|
||||
case '[': // section marker
|
||||
|
||||
if( (aString = strchr(aLine, ']')) ) |
||||
{ |
||||
*aString = '\0'; |
||||
|
||||
// accept as matched if key exact match
|
||||
|
||||
if(!strcmp(aLine + 1, theSection)) |
||||
{ |
||||
aSectionFound = TRUE; |
||||
} |
||||
} |
||||
|
||||
break; |
||||
|
||||
default: |
||||
|
||||
// try to match value keys if in proper section
|
||||
|
||||
if(aSectionFound) |
||||
{ |
||||
// try to match requested key
|
||||
|
||||
if( (aString = aValue = strchr(aLine, '=')) ) |
||||
{ |
||||
*aValue = '\0'; |
||||
++aValue; |
||||
|
||||
// strip leading blanks in value field
|
||||
|
||||
while(*aValue == ' ' && aValue < aLine + sizeof(aLine)) |
||||
{ |
||||
*aValue++ = '\0'; |
||||
} |
||||
if(aValue >= aLine + sizeof(aLine)) |
||||
{ |
||||
aValue = ""; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
aValue = ""; |
||||
} |
||||
|
||||
// strip trailing blanks from key
|
||||
|
||||
if(aString) |
||||
{ |
||||
while(--aString >= aLine && *aString == ' ') |
||||
{ |
||||
*aString = '\0'; |
||||
} |
||||
} |
||||
|
||||
// see if key is matched
|
||||
|
||||
if(!strcmp(theKey, aLine)) |
||||
{ |
||||
keyFound = TRUE; |
||||
// matched -- first, terminate value part
|
||||
|
||||
// overwrite current value
|
||||
fseek(aFile,-aLineLength,SEEK_CUR); |
||||
// overwrite key and value
|
||||
sprintf(aLine,"%s = %s\n",theKey,theBuffer); |
||||
fputs(aLine,aFile); |
||||
} |
||||
} |
||||
} |
||||
|
||||
break; |
||||
} |
||||
} |
||||
|
||||
if(!keyFound) { // theKey wasn't in file so
|
||||
if(aFile) |
||||
{ |
||||
fclose(aFile); |
||||
} |
||||
|
||||
return aReturnLength > 0 ? aReturnLength - 1 : 0; |
||||
} |
||||
*/ |
||||
|
||||
#endif |
||||
@ -0,0 +1,40 @@ |
||||
// GetPrivateProfileString
|
||||
// for UNIX use
|
||||
#ifndef GPPS_H |
||||
#define GPPS_H |
||||
|
||||
#ifdef HAVE_CONFIG_H |
||||
#include <config.h> |
||||
#endif |
||||
|
||||
#ifdef UNIX |
||||
#include <sys/types.h> |
||||
#include "iodbc.h" |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
DWORD |
||||
GetPrivateProfileString(char *theSection, // section name
|
||||
char *theKey, // search key name
|
||||
char *theDefault, // default value if not found
|
||||
char *theReturnBuffer, // return valuse stored here
|
||||
size_t theBufferLength, // byte length of return buffer
|
||||
char *theIniFileName); // pathname of ini file to search
|
||||
|
||||
DWORD |
||||
WritePrivateProfileString(char *theSection, // section name
|
||||
char *theKey, // write key name
|
||||
char *theBuffer, // input buffer
|
||||
char *theIniFileName); // pathname of ini file to write
|
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#ifdef UNIX |
||||
#undef DWORD |
||||
#endif |
||||
#endif |
||||
@ -0,0 +1,66 @@ |
||||
#ifndef _IODBC_H |
||||
#define _IODBC_H |
||||
|
||||
# if !defined(WINDOWS) && !defined(WIN32_SYSTEM) |
||||
# define _UNIX_ |
||||
|
||||
# include <stdlib.h> |
||||
# include <sys/types.h> |
||||
|
||||
# define MEM_ALLOC(size) (malloc((size_t)(size))) |
||||
# define MEM_FREE(ptr) {if(ptr) free(ptr);} |
||||
|
||||
# define STRCPY(t, s) (strcpy((char*)(t), (char*)(s))) |
||||
# define STRNCPY(t,s,n) (strncpy((char*)(t), (char*)(s), (size_t)(n))) |
||||
# define STRCAT(t, s) (strcat((char*)(t), (char*)(s))) |
||||
# define STRNCAT(t,s,n) (strncat((char*)(t), (char*)(s), (size_t)(n))) |
||||
# define STREQ(a, b) (strcmp((char*)(a), (char*)(b)) == 0) |
||||
# define STRLEN(str) ((str)? strlen((char*)(str)):0) |
||||
|
||||
# define EXPORT |
||||
# define CALLBACK |
||||
# define FAR |
||||
|
||||
typedef signed short SSHOR; |
||||
typedef short WORD; |
||||
typedef long DWORD; |
||||
|
||||
typedef WORD WPARAM; |
||||
typedef DWORD LPARAM; |
||||
typedef void* HWND; |
||||
typedef int BOOL; |
||||
|
||||
# endif /* _UNIX_ */ |
||||
|
||||
# if defined(WINDOWS) || defined(WIN32_SYSTEM) |
||||
|
||||
# include <windows.h> |
||||
# include <windowsx.h> |
||||
|
||||
# ifdef _MSVC_ |
||||
# define MEM_ALLOC(size) (fmalloc((size_t)(size))) |
||||
# define MEM_FREE(ptr) ((ptr)? ffree((PTR)(ptr)):0)) |
||||
# define STRCPY(t, s) (fstrcpy((char FAR*)(t), (char FAR*)(s))) |
||||
# define STRNCPY(t,s,n) (fstrncpy((char FAR*)(t), (char FAR*)(s), (size_t)(n))) |
||||
# define STRLEN(str) ((str)? fstrlen((char FAR*)(str)):0) |
||||
# define STREQ(a, b) (fstrcmp((char FAR*)(a), (char FAR*)(b) == 0) |
||||
# endif |
||||
|
||||
# ifdef _BORLAND_ |
||||
# define MEM_ALLOC(size) (farmalloc((unsigned long)(size)) |
||||
# define MEM_FREE(ptr) ((ptr)? farfree((void far*)(ptr)):0) |
||||
# define STRCPY(t, s) (_fstrcpy((char FAR*)(t), (char FAR*)(s))) |
||||
# define STRNCPY(t,s,n) (_fstrncpy((char FAR*)(t), (char FAR*)(s), (size_t)(n))) |
||||
# define STRLEN(str) ((str)? _fstrlen((char FAR*)(str)):0) |
||||
# define STREQ(a, b) (_fstrcmp((char FAR*)(a), (char FAR*)(b) == 0) |
||||
# endif |
||||
|
||||
# endif /* WINDOWS */ |
||||
|
||||
# define SYSERR (-1) |
||||
|
||||
# ifndef NULL |
||||
# define NULL ((void FAR*)0UL) |
||||
# endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,237 @@ |
||||
/* Modified isql.h file from iodbc. This file should be placed in the
|
||||
* include path to be used to create ODBC compliant applications. |
||||
*/ |
||||
#ifndef _INTRINSIC_SQL_H |
||||
# define _INTRINSIC_SQL_H |
||||
|
||||
typedef unsigned char UCHAR; |
||||
typedef long int SDWORD; |
||||
typedef short int SWORD; |
||||
typedef unsigned long int UDWORD;
|
||||
typedef unsigned short int UWORD; |
||||
|
||||
typedef void FAR* PTR; |
||||
|
||||
typedef void FAR* HENV; |
||||
typedef void FAR* HDBC; |
||||
typedef void FAR* HSTMT; |
||||
|
||||
typedef signed short RETCODE; |
||||
|
||||
# ifdef WIN32 |
||||
# define SQL_API __stdcall |
||||
# else |
||||
# define SQL_API EXPORT CALLBACK |
||||
# endif |
||||
|
||||
# define ODBCVER 0x0200 |
||||
|
||||
# define SQL_MAX_MESSAGE_LENGTH 512 |
||||
# define SQL_MAX_DSN_LENGTH 32 |
||||
|
||||
/* return code */ |
||||
# define SQL_INVALID_HANDLE (-2) |
||||
# define SQL_ERROR (-1) |
||||
# define SQL_SUCCESS 0 |
||||
# define SQL_SUCCESS_WITH_INFO 1 |
||||
# define SQL_NO_DATA_FOUND 100 |
||||
|
||||
/* standard SQL datatypes (agree with ANSI type numbering) */ |
||||
# define SQL_CHAR 1 |
||||
# define SQL_NUMERIC 2 |
||||
# define SQL_DECIMAL 3 |
||||
# define SQL_INTEGER 4 |
||||
# define SQL_SMALLINT 5 |
||||
# define SQL_FLOAT 6 |
||||
# define SQL_REAL 7 |
||||
# define SQL_DOUBLE 8 |
||||
# define SQL_VARCHAR 12 |
||||
|
||||
# define SQL_TYPE_MIN SQL_CHAR |
||||
# define SQL_TYPE_NULL 0 |
||||
# define SQL_TYPE_MAX SQL_VARCHAR |
||||
|
||||
/* C to SQL datatype mapping */ |
||||
# define SQL_C_CHAR SQL_CHAR |
||||
# define SQL_C_LONG SQL_INTEGER |
||||
# define SQL_C_SHORT SQL_SMALLINT |
||||
# define SQL_C_FLOAT SQL_REAL |
||||
# define SQL_C_DOUBLE SQL_DOUBLE |
||||
# define SQL_C_DEFAULT 99 |
||||
|
||||
# define SQL_NO_NULLS 0 |
||||
# define SQL_NULLABLE 1 |
||||
# define SQL_NULLABLE_UNKNOWN 2 |
||||
|
||||
/* some special length values */ |
||||
# define SQL_NULL_DATA (-1) |
||||
# define SQL_DATA_AT_EXEC (-2) |
||||
# define SQL_NTS (-3) |
||||
|
||||
/* SQLFreeStmt flag values */ |
||||
# define SQL_CLOSE 0 |
||||
# define SQL_DROP 1 |
||||
# define SQL_UNBIND 2 |
||||
# define SQL_RESET_PARAMS 3 |
||||
|
||||
/* SQLTransact flag values */ |
||||
# define SQL_COMMIT 0 |
||||
# define SQL_ROLLBACK 1 |
||||
|
||||
/* SQLColAttributes flag values */ |
||||
# define SQL_COLUMN_COUNT 0 |
||||
# define SQL_COLUMN_LABEL 18 |
||||
# define SQL_COLATT_OPT_MAX SQL_COLUMN_LABEL |
||||
# define SQL_COLUMN_DRIVER_START 1000 |
||||
|
||||
# define SQL_COLATT_OPT_MIN SQL_COLUMN_COUNT |
||||
|
||||
/* Null handles */ |
||||
# define SQL_NULL_HENV 0 |
||||
# define SQL_NULL_HDBC 0 |
||||
# define SQL_NULL_HSTMT 0 |
||||
|
||||
/* All code below has been added to the original isql.h coming from iodbc */ |
||||
typedef unsigned char BYTE; |
||||
|
||||
/* More SQLColAttributes flag values */ |
||||
#define SQL_COLUMN_NAME 1 |
||||
#define SQL_COLUMN_TYPE 2 |
||||
#define SQL_COLUMN_LENGTH 3 |
||||
#define SQL_COLUMN_PRECISION 4 |
||||
#define SQL_COLUMN_SCALE 5 |
||||
#define SQL_COLUMN_DISPLAY_SIZE 6 |
||||
#define SQL_COLUMN_NULLABLE 7 |
||||
#define SQL_COLUMN_UNSIGNED 8 |
||||
#define SQL_COLUMN_MONEY 9 |
||||
#define SQL_COLUMN_UPDATABLE 10 |
||||
#define SQL_COLUMN_AUTO_INCREMENT 11 |
||||
#define SQL_COLUMN_CASE_SENSITIVE 12 |
||||
#define SQL_COLUMN_SEARCHABLE 13 |
||||
#define SQL_COLUMN_TYPE_NAME 14 |
||||
#define SQL_COLUMN_TABLE_NAME 15 |
||||
#define SQL_COLUMN_OWNER_NAME 16 |
||||
#define SQL_COLUMN_QUALIFIER_NAME 17 |
||||
|
||||
/* SQLColAttributes Searchable flags */ |
||||
#define SQL_UNSEARCHABLE 0 |
||||
#define SQL_LIKE_ONLY 1 |
||||
#define SQL_ALL_EXCEPT_LIKE 2 |
||||
#define SQL_SEARCHABLE 3 |
||||
#define SQL_PRED_SEARCHABLE SQL_SEARCHABLE |
||||
|
||||
/* SQLColAttributes Updateable flags */ |
||||
#define SQL_ATTR_READONLY 0 |
||||
#define SQL_ATTR_WRITE 1 |
||||
#define SQL_ATTR_READWRITE_UNKNOWN 2 |
||||
|
||||
/*
|
||||
* function prototypes previously not contained in isql.h |
||||
*/ |
||||
#ifdef __cplusplus |
||||
extern "C" |
||||
{ |
||||
#endif |
||||
|
||||
RETCODE SQL_API SQLAllocConnect (HENV henv, |
||||
HDBC FAR * phdbc); |
||||
RETCODE SQL_API SQLAllocEnv (HENV FAR * phenv); |
||||
RETCODE SQL_API SQLAllocStmt (HDBC hdbc, |
||||
HSTMT FAR * phstmt); |
||||
RETCODE SQL_API SQLBindCol (HSTMT hstmt, |
||||
UWORD icol, |
||||
SWORD fCType, |
||||
PTR rgbValue, |
||||
SDWORD cbValueMax, |
||||
SDWORD FAR * pcbValue); |
||||
|
||||
RETCODE SQL_API SQLCancel (HSTMT hstmt); |
||||
|
||||
RETCODE SQL_API SQLColAttributes (HSTMT hstmt, |
||||
UWORD icol, |
||||
UWORD fDescType, |
||||
PTR rgbDesc, |
||||
SWORD cbDescMax, |
||||
SWORD FAR * pcbDesc, |
||||
SDWORD FAR * pfDesc); |
||||
|
||||
RETCODE SQL_API SQLConnect (HDBC hdbc, |
||||
UCHAR FAR * szDSN, |
||||
SWORD cbDSN, |
||||
UCHAR FAR * szUID, |
||||
SWORD cbUID, |
||||
UCHAR FAR * szAuthStr, |
||||
SWORD cbAuthStr); |
||||
|
||||
RETCODE SQL_API SQLDescribeCol (HSTMT hstmt, |
||||
UWORD icol, |
||||
UCHAR FAR * szColName, |
||||
SWORD cbColNameMax, |
||||
SWORD FAR * pcbColName, |
||||
SWORD FAR * pfSqlType, |
||||
UDWORD FAR * pcbColDef, |
||||
SWORD FAR * pibScale, |
||||
SWORD FAR * pfNullable); |
||||
|
||||
RETCODE SQL_API SQLDisconnect (HDBC hdbc); |
||||
|
||||
RETCODE SQL_API SQLError (HENV henv, |
||||
HDBC hdbc, |
||||
HSTMT hstmt, |
||||
UCHAR FAR * szSqlState, |
||||
SDWORD FAR * pfNativeError, |
||||
UCHAR FAR * szErrorMsg, |
||||
SWORD cbErrorMsgMax, |
||||
SWORD FAR * pcbErrorMsg); |
||||
|
||||
RETCODE SQL_API SQLExecDirect (HSTMT hstmt, |
||||
UCHAR FAR * szSqlStr, |
||||
SDWORD cbSqlStr); |
||||
|
||||
RETCODE SQL_API SQLExecute (HSTMT hstmt); |
||||
|
||||
RETCODE SQL_API SQLFetch (HSTMT hstmt); |
||||
|
||||
RETCODE SQL_API SQLFreeConnect (HDBC hdbc); |
||||
|
||||
RETCODE SQL_API SQLFreeEnv (HENV henv); |
||||
|
||||
RETCODE SQL_API SQLFreeStmt (HSTMT hstmt, |
||||
UWORD fOption); |
||||
|
||||
RETCODE SQL_API SQLGetCursorName (HSTMT hstmt, |
||||
UCHAR FAR * szCursor, |
||||
SWORD cbCursorMax, |
||||
SWORD FAR * pcbCursor); |
||||
|
||||
RETCODE SQL_API SQLNumResultCols (HSTMT hstmt, |
||||
SWORD FAR * pccol); |
||||
|
||||
RETCODE SQL_API SQLPrepare (HSTMT hstmt, |
||||
UCHAR FAR * szSqlStr, |
||||
SDWORD cbSqlStr); |
||||
|
||||
RETCODE SQL_API SQLRowCount (HSTMT hstmt, |
||||
SDWORD FAR * pcrow); |
||||
|
||||
RETCODE SQL_API SQLSetCursorName (HSTMT hstmt, |
||||
UCHAR FAR * szCursor, |
||||
SWORD cbCursor); |
||||
|
||||
RETCODE SQL_API SQLTransact (HENV henv, |
||||
HDBC hdbc, |
||||
UWORD fType); |
||||
|
||||
RETCODE SQL_API SQLSetParam (HSTMT hstmt, |
||||
UWORD ipar, |
||||
SWORD fCType, |
||||
SWORD fSqlType, |
||||
UDWORD cbColDef, |
||||
SWORD ibScale, |
||||
PTR rgbValue, |
||||
SDWORD FAR * pcbValue); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
#endif |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@ |
||||
[PostgreSQL] |
||||
Debug = 0 |
||||
CommLog = 1 |
||||
@ -0,0 +1,775 @@ |
||||
|
||||
/* Module: parse.c
|
||||
* |
||||
* Description: This module contains routines related to parsing SQL statements. |
||||
* This can be useful for two reasons: |
||||
* |
||||
* 1. So the query does not actually have to be executed to return data about it |
||||
* |
||||
* 2. To be able to return information about precision, nullability, aliases, etc. |
||||
* in the functions SQLDescribeCol and SQLColAttributes. Currently, Postgres |
||||
* doesn't return any information about these things in a query. |
||||
* |
||||
* Classes: none |
||||
* |
||||
* API functions: none |
||||
* |
||||
* Comments: See "notice.txt" for copyright and license information. |
||||
* |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#include "statement.h" |
||||
#include "connection.h" |
||||
#include "qresult.h" |
||||
#include "pgtypes.h" |
||||
|
||||
#ifdef UNIX |
||||
#if !HAVE_STRICMP |
||||
#define stricmp(s1,s2) strcasecmp(s1,s2) |
||||
#define strnicmp(s1,s2,n) strncasecmp(s1,s2,n) |
||||
#endif |
||||
#endif |
||||
|
||||
#define FLD_INCR 32 |
||||
#define TAB_INCR 8 |
||||
#define COL_INCR 16 |
||||
|
||||
char * |
||||
getNextToken(char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric) |
||||
{ |
||||
int i = 0; |
||||
int out = 0; |
||||
char qc, in_escape = FALSE; |
||||
|
||||
if (smax <= 1) |
||||
return NULL; |
||||
|
||||
smax--; |
||||
|
||||
/* skip leading delimiters */ |
||||
while (isspace(s[i]) || s[i] == ',') { |
||||
// mylog("skipping '%c'\n", s[i]);
|
||||
i++; |
||||
} |
||||
|
||||
if (s[0] == '\0') { |
||||
token[0] = '\0'; |
||||
return NULL; |
||||
} |
||||
|
||||
if (quote) *quote = FALSE; |
||||
if (dquote) *dquote = FALSE; |
||||
if (numeric) *numeric = FALSE; |
||||
|
||||
/* get the next token */ |
||||
while ( ! isspace(s[i]) && s[i] != ',' && s[i] != '\0' && out != smax) { |
||||
|
||||
/* Handle quoted stuff */ |
||||
if ( out == 0 && (s[i] == '\"' || s[i] == '\'')) { |
||||
qc = s[i]; |
||||
if (qc == '\"') { |
||||
if (dquote) *dquote = TRUE; |
||||
} |
||||
if (qc == '\'') { |
||||
if (quote) *quote = TRUE; |
||||
} |
||||
|
||||
i++; /* dont return the quote */ |
||||
while (s[i] != '\0' && out != smax) {
|
||||
if (s[i] == qc && ! in_escape) { |
||||
break; |
||||
} |
||||
if (s[i] == '\\' && ! in_escape) { |
||||
in_escape = TRUE; |
||||
} |
||||
else { |
||||
in_escape = FALSE;
|
||||
token[out++] = s[i]; |
||||
} |
||||
i++; |
||||
} |
||||
if (s[i] == qc) |
||||
i++; |
||||
break; |
||||
} |
||||
|
||||
/* Check for numeric literals */ |
||||
if ( out == 0 && isdigit(s[i])) { |
||||
if (numeric) *numeric = TRUE; |
||||
token[out++] = s[i++]; |
||||
while ( isalnum(s[i]) || s[i] == '.') |
||||
token[out++] = s[i++]; |
||||
|
||||
break; |
||||
} |
||||
|
||||
if ( ispunct(s[i])) { |
||||
if (out == 0) { |
||||
token[out++] = s[i++]; |
||||
break; |
||||
} |
||||
else |
||||
break; |
||||
} |
||||
|
||||
if (out != smax) |
||||
token[out++] = s[i]; |
||||
|
||||
i++; |
||||
} |
||||
|
||||
// mylog("done -- s[%d] = '%c'\n", i, s[i]);
|
||||
|
||||
token[out] = '\0'; |
||||
|
||||
/* find the delimiter */ |
||||
while ( isspace(s[i])) |
||||
i++; |
||||
|
||||
/* return the most priority delimiter */ |
||||
if (s[i] == ',') { |
||||
if (delim) *delim = s[i]; |
||||
} |
||||
else if (s[i] == '\0') { |
||||
if (delim) *delim = '\0'; |
||||
} |
||||
else { |
||||
if (delim) *delim = ' '; |
||||
} |
||||
|
||||
/* skip trailing blanks */ |
||||
while ( isspace(s[i])) { |
||||
i++; |
||||
} |
||||
|
||||
return &s[i]; |
||||
} |
||||
|
||||
|
||||
/*
|
||||
QR_set_num_fields(stmt->result, 14); |
||||
QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING); |
||||
QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING); |
||||
QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING); |
||||
QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING); |
||||
QR_set_field_info(stmt->result, 4, "DATA_TYPE", PG_TYPE_INT2, 2); |
||||
QR_set_field_info(stmt->result, 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING); |
||||
QR_set_field_info(stmt->result, 6, "PRECISION", PG_TYPE_INT4, 4); |
||||
QR_set_field_info(stmt->result, 7, "LENGTH", PG_TYPE_INT4, 4); |
||||
QR_set_field_info(stmt->result, 8, "SCALE", PG_TYPE_INT2, 2); |
||||
QR_set_field_info(stmt->result, 9, "RADIX", PG_TYPE_INT2, 2); |
||||
QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2); |
||||
QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254); |
||||
// User defined fields
|
||||
QR_set_field_info(stmt->result, 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4); |
||||
QR_set_field_info(stmt->result, 13, "FIELD_TYPE", PG_TYPE_INT4, 4); |
||||
*/ |
||||
|
||||
void |
||||
getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k) |
||||
{ |
||||
if (fi->name[0] == '\0') |
||||
strcpy(fi->name, QR_get_value_manual(col_info->result, k, 3)); |
||||
|
||||
fi->type = atoi( QR_get_value_manual(col_info->result, k, 13)); |
||||
fi->precision = atoi( QR_get_value_manual(col_info->result, k, 6)); |
||||
fi->length = atoi( QR_get_value_manual(col_info->result, k, 7)); |
||||
fi->nullable = atoi( QR_get_value_manual(col_info->result, k, 10)); |
||||
fi->display_size = atoi( QR_get_value_manual(col_info->result, k, 12)); |
||||
} |
||||
|
||||
char |
||||
searchColInfo(COL_INFO *col_info, FIELD_INFO *fi) |
||||
{ |
||||
int k; |
||||
char *col; |
||||
|
||||
|
||||
for (k = 0; k < QR_get_num_tuples(col_info->result); k++) { |
||||
col = QR_get_value_manual(col_info->result, k, 3); |
||||
if ( ! strcmp(col, fi->name)) { |
||||
getColInfo(col_info, fi, k); |
||||
|
||||
mylog("PARSE: searchColInfo: \n"); |
||||
return TRUE; |
||||
} |
||||
} |
||||
|
||||
return FALSE; |
||||
} |
||||
|
||||
|
||||
char |
||||
parse_statement(StatementClass *stmt) |
||||
{ |
||||
char token[256]; |
||||
char delim, quote, dquote, numeric, unquoted; |
||||
char *ptr; |
||||
char in_select = FALSE, in_distinct = FALSE, in_on = FALSE, in_from = FALSE, in_where = FALSE, in_table = FALSE; |
||||
char in_field = FALSE, in_expr = FALSE, in_func = FALSE, in_dot = FALSE, in_as = FALSE; |
||||
int j, i, k, n, blevel = 0; |
||||
FIELD_INFO **fi; |
||||
TABLE_INFO **ti; |
||||
char parse; |
||||
ConnectionClass *conn = stmt->hdbc; |
||||
HSTMT hcol_stmt; |
||||
StatementClass *col_stmt; |
||||
RETCODE result; |
||||
|
||||
|
||||
ptr = stmt->statement; |
||||
fi = stmt->fi; |
||||
ti = stmt->ti; |
||||
|
||||
stmt->nfld = 0; |
||||
stmt->ntab = 0; |
||||
|
||||
while (ptr = getNextToken(ptr, token, sizeof(token), &delim, "e, &dquote, &numeric)) { |
||||
|
||||
unquoted = ! ( quote || dquote ); |
||||
|
||||
mylog("unquoted=%d, quote=%d, dquote=%d, numeric=%d, delim='%c', token='%s', ptr='%s'\n", unquoted, quote, dquote, numeric, delim, token, ptr); |
||||
|
||||
if ( unquoted && ! stricmp(token, "select")) { |
||||
in_select = TRUE; |
||||
|
||||
mylog("SELECT\n"); |
||||
continue; |
||||
} |
||||
|
||||
if ( unquoted && in_select && ! stricmp(token, "distinct")) { |
||||
in_distinct = TRUE; |
||||
|
||||
mylog("DISTINCT\n"); |
||||
continue; |
||||
} |
||||
|
||||
if ( unquoted && ! stricmp(token, "into")) { |
||||
in_select = FALSE; |
||||
|
||||
mylog("INTO\n"); |
||||
continue; |
||||
} |
||||
|
||||
if ( unquoted && ! stricmp(token, "from")) { |
||||
in_select = FALSE; |
||||
in_from = TRUE; |
||||
|
||||
mylog("FROM\n"); |
||||
continue; |
||||
} |
||||
|
||||
if ( unquoted && (! stricmp(token, "where") || |
||||
! stricmp(token, "union") ||
|
||||
! stricmp(token, "order") ||
|
||||
! stricmp(token, "group") ||
|
||||
! stricmp(token, "having"))) { |
||||
|
||||
in_select = FALSE; |
||||
in_from = FALSE; |
||||
in_where = TRUE; |
||||
|
||||
mylog("WHERE...\n"); |
||||
break; |
||||
} |
||||
|
||||
if (in_select) { |
||||
|
||||
if ( in_distinct) { |
||||
mylog("in distinct\n"); |
||||
|
||||
if (unquoted && ! stricmp(token, "on")) { |
||||
in_on = TRUE; |
||||
mylog("got on\n"); |
||||
continue; |
||||
} |
||||
if (in_on) { |
||||
in_distinct = FALSE; |
||||
in_on = FALSE; |
||||
continue; /* just skip the unique on field */ |
||||
} |
||||
mylog("done distinct\n"); |
||||
in_distinct = FALSE; |
||||
} |
||||
|
||||
if ( in_expr || in_func) { /* just eat the expression */ |
||||
mylog("in_expr=%d or func=%d\n", in_expr, in_func); |
||||
if (quote || dquote) |
||||
continue; |
||||
|
||||
if (in_expr && blevel == 0 && delim == ',') { |
||||
mylog("**** in_expr and Got comma\n"); |
||||
in_expr = FALSE; |
||||
in_field = FALSE; |
||||
} |
||||
|
||||
else if (token[0] == '(') { |
||||
blevel++; |
||||
mylog("blevel++ = %d\n", blevel); |
||||
} |
||||
else if (token[0] == ')') { |
||||
blevel--; |
||||
mylog("blevel-- = %d\n", blevel); |
||||
if (delim==',') { |
||||
in_func = FALSE; |
||||
in_expr = FALSE; |
||||
in_field = FALSE; |
||||
} |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
if ( ! in_field) { |
||||
|
||||
if ( ! token[0]) |
||||
continue; |
||||
|
||||
if ( ! (stmt->nfld % FLD_INCR)) { |
||||
mylog("reallocing at nfld=%d\n", stmt->nfld); |
||||
fi = (FIELD_INFO **) realloc(fi, (stmt->nfld + FLD_INCR) * sizeof(FIELD_INFO *)); |
||||
if ( ! fi) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
stmt->fi = fi; |
||||
} |
||||
|
||||
fi[stmt->nfld] = (FIELD_INFO *) malloc( sizeof(FIELD_INFO)); |
||||
if (fi[stmt->nfld] == NULL) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
|
||||
/* Initialize the field info */ |
||||
memset(fi[stmt->nfld], 0, sizeof(FIELD_INFO)); |
||||
|
||||
/* double quotes are for qualifiers */ |
||||
if (dquote) |
||||
fi[stmt->nfld]->dquote = TRUE; |
||||
|
||||
if (quote) { |
||||
fi[stmt->nfld++]->quote = TRUE; |
||||
continue; |
||||
} |
||||
else if (numeric) { |
||||
mylog("**** got numeric: nfld = %d\n", stmt->nfld); |
||||
fi[stmt->nfld]->numeric = TRUE; |
||||
} |
||||
else if (token[0] == '(') { /* expression */ |
||||
mylog("got EXPRESSION\n"); |
||||
fi[stmt->nfld++]->expr = TRUE; |
||||
in_expr = TRUE; |
||||
blevel = 1; |
||||
continue; |
||||
} |
||||
|
||||
else { |
||||
strcpy(fi[stmt->nfld]->name, token); |
||||
fi[stmt->nfld]->dot[0] = '\0'; |
||||
} |
||||
mylog("got field='%s', dot='%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->dot); |
||||
|
||||
if (delim == ',') { |
||||
mylog("comma (1)\n"); |
||||
} |
||||
else { |
||||
in_field = TRUE; |
||||
} |
||||
stmt->nfld++; |
||||
continue; |
||||
} |
||||
|
||||
/**************************/ |
||||
/* We are in a field now */ |
||||
/**************************/ |
||||
if (in_dot) { |
||||
stmt->nfld--; |
||||
strcpy(fi[stmt->nfld]->dot, fi[stmt->nfld]->name); |
||||
strcpy(fi[stmt->nfld]->name, token); |
||||
stmt->nfld++; |
||||
in_dot = FALSE; |
||||
|
||||
if (delim == ',') { |
||||
mylog("in_dot: got comma\n"); |
||||
in_field = FALSE; |
||||
} |
||||
|
||||
continue; |
||||
} |
||||
|
||||
if (in_as) { |
||||
stmt->nfld--; |
||||
strcpy(fi[stmt->nfld]->alias, token); |
||||
mylog("alias for field '%s' is '%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->alias); |
||||
in_as = FALSE; |
||||
in_field = FALSE; |
||||
|
||||
stmt->nfld++; |
||||
|
||||
if (delim == ',') { |
||||
mylog("comma(2)\n"); |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
/* Function */ |
||||
if (token[0] == '(') { |
||||
in_func = TRUE; |
||||
blevel = 1; |
||||
fi[stmt->nfld-1]->func = TRUE; |
||||
/* name will have the function name -- maybe useful some day */ |
||||
mylog("**** got function = '%s'\n", fi[stmt->nfld-1]->name); |
||||
continue; |
||||
} |
||||
|
||||
if (token[0] == '.') { |
||||
in_dot = TRUE;
|
||||
mylog("got dot\n"); |
||||
continue; |
||||
} |
||||
|
||||
if ( ! stricmp(token, "as")) { |
||||
in_as = TRUE; |
||||
mylog("got AS\n"); |
||||
continue; |
||||
} |
||||
|
||||
/* otherwise, its probably an expression */ |
||||
in_expr = TRUE; |
||||
fi[stmt->nfld-1]->expr = TRUE; |
||||
fi[stmt->nfld-1]->name[0] = '\0'; |
||||
mylog("*** setting expression\n"); |
||||
|
||||
} |
||||
|
||||
if (in_from) { |
||||
|
||||
if ( ! in_table) { |
||||
if ( ! token[0]) |
||||
continue; |
||||
|
||||
if ( ! (stmt->ntab % TAB_INCR)) { |
||||
ti = (TABLE_INFO **) realloc(ti, (stmt->ntab + TAB_INCR) * sizeof(TABLE_INFO *)); |
||||
if ( ! ti) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
stmt->ti = ti; |
||||
} |
||||
ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO)); |
||||
if (ti[stmt->ntab] == NULL) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
|
||||
ti[stmt->ntab]->alias[0] = '\0'; |
||||
|
||||
strcpy(ti[stmt->ntab]->name, token); |
||||
mylog("got table = '%s'\n", ti[stmt->ntab]->name); |
||||
|
||||
if (delim == ',') { |
||||
mylog("more than 1 tables\n"); |
||||
} |
||||
else { |
||||
in_table = TRUE; |
||||
} |
||||
stmt->ntab++; |
||||
continue; |
||||
} |
||||
|
||||
strcpy(ti[stmt->ntab-1]->alias, token); |
||||
mylog("alias for table '%s' is '%s'\n", ti[stmt->ntab-1]->name, ti[stmt->ntab-1]->alias); |
||||
in_table = FALSE; |
||||
if (delim == ',') { |
||||
mylog("more than 1 tables\n"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/*************************************************/ |
||||
/* Resolve any possible field names with tables */ |
||||
/*************************************************/ |
||||
|
||||
parse = TRUE; |
||||
|
||||
/* Resolve field names with tables */ |
||||
for (i = 0; i < stmt->nfld; i++) { |
||||
|
||||
if (fi[i]->func || fi[i]->expr || fi[i]->numeric) { |
||||
fi[i]->ti = NULL; |
||||
fi[i]->type = -1; |
||||
parse = FALSE; |
||||
continue; |
||||
} |
||||
|
||||
else if (fi[i]->quote) { /* handle as text */ |
||||
fi[i]->ti = NULL; |
||||
fi[i]->type = PG_TYPE_TEXT; |
||||
fi[i]->precision = 0; |
||||
continue; |
||||
}
|
||||
|
||||
/* its a dot, resolve to table or alias */ |
||||
else if (fi[i]->dot[0]) { |
||||
for (k = 0; k < stmt->ntab; k++) { |
||||
if ( ! stricmp(ti[k]->name, fi[i]->dot)) { |
||||
fi[i]->ti = ti[k]; |
||||
break; |
||||
} |
||||
else if ( ! stricmp(ti[k]->alias, fi[i]->dot)) { |
||||
fi[i]->ti = ti[k]; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
else if (stmt->ntab == 1) |
||||
fi[i]->ti = ti[0]; |
||||
} |
||||
|
||||
mylog("--------------------------------------------\n"); |
||||
mylog("nfld=%d, ntab=%d\n", stmt->nfld, stmt->ntab); |
||||
|
||||
for (i=0; i < stmt->nfld; i++) { |
||||
mylog("Field %d: expr=%d, func=%d, quote=%d, dquote=%d, numeric=%d, name='%s', alias='%s', dot='%s'\n", i, fi[i]->expr, fi[i]->func, fi[i]->quote, fi[i]->dquote, fi[i]->numeric, fi[i]->name, fi[i]->alias, fi[i]->dot); |
||||
if (fi[i]->ti) |
||||
mylog(" ----> table_name='%s', table_alias='%s'\n", fi[i]->ti->name, fi[i]->ti->alias); |
||||
} |
||||
|
||||
for (i=0; i < stmt->ntab; i++) { |
||||
mylog("Table %d: name='%s', alias='%s'\n", i, ti[i]->name, ti[i]->alias); |
||||
} |
||||
|
||||
|
||||
/******************************************************/ |
||||
/* Now save the SQLColumns Info for the parse tables */ |
||||
/******************************************************/ |
||||
|
||||
|
||||
/* Call SQLColumns for each table and store the result */ |
||||
for (i = 0; i < stmt->ntab; i++) { |
||||
|
||||
/* See if already got it */ |
||||
char found = FALSE; |
||||
for (k = 0; k < conn->ntables; k++) { |
||||
if ( ! stricmp(conn->col_info[k]->name, ti[i]->name)) { |
||||
mylog("FOUND col_info table='%s'\n", ti[i]->name); |
||||
found = TRUE; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if ( ! found) { |
||||
|
||||
mylog("PARSE: Getting SQLColumns for table[%d]='%s'\n", i, ti[i]->name); |
||||
|
||||
result = SQLAllocStmt( stmt->hdbc, &hcol_stmt); |
||||
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { |
||||
stmt->errormsg = "SQLAllocStmt failed in parse_statement for columns."; |
||||
stmt->errornumber = STMT_NO_MEMORY_ERROR; |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
|
||||
col_stmt = (StatementClass *) hcol_stmt; |
||||
col_stmt->internal = TRUE; |
||||
|
||||
result = SQLColumns(hcol_stmt, "", 0, "", 0,
|
||||
ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0); |
||||
|
||||
mylog(" Past SQLColumns\n"); |
||||
if (result == SQL_SUCCESS) { |
||||
mylog(" Success\n"); |
||||
if ( ! (conn->ntables % COL_INCR)) { |
||||
|
||||
mylog("PARSE: Allocing col_info at ntables=%d\n", conn->ntables); |
||||
|
||||
conn->col_info = (COL_INFO **) realloc(conn->col_info, (conn->ntables + COL_INCR) * sizeof(COL_INFO *)); |
||||
if ( ! conn->col_info) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
} |
||||
|
||||
mylog("PARSE: malloc at conn->col_info[%d]\n", conn->ntables); |
||||
conn->col_info[conn->ntables] = (COL_INFO *) malloc(sizeof(COL_INFO)); |
||||
if ( ! conn->col_info[conn->ntables]) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
|
||||
/* Store the table name and the SQLColumns result structure */ |
||||
strcpy(conn->col_info[conn->ntables]->name, ti[i]->name); |
||||
conn->col_info[conn->ntables]->result = col_stmt->result; |
||||
|
||||
/* The connection will now free the result structures, so make
|
||||
sure that the statement doesn't free it
|
||||
*/ |
||||
col_stmt->result = NULL; |
||||
|
||||
conn->ntables++; |
||||
|
||||
SQLFreeStmt(hcol_stmt, SQL_DROP); |
||||
mylog("Created col_info table='%s', ntables=%d\n", ti[i]->name, conn->ntables); |
||||
} |
||||
else { |
||||
SQLFreeStmt(hcol_stmt, SQL_DROP); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/* Associate a table from the statement with a SQLColumn info */ |
||||
ti[i]->col_info = conn->col_info[k]; |
||||
mylog("associate col_info: i=%d, k=%d\n", i, k); |
||||
} |
||||
|
||||
|
||||
mylog("Done SQLColumns\n"); |
||||
|
||||
/******************************************************/ |
||||
/* Now resolve the fields to point to column info */ |
||||
/******************************************************/ |
||||
|
||||
|
||||
|
||||
for (i = 0; i < stmt->nfld;) { |
||||
|
||||
/* Dont worry about functions or quotes */ |
||||
if (fi[i]->func || fi[i]->quote || fi[i]->numeric) { |
||||
i++; |
||||
continue; |
||||
} |
||||
|
||||
/* Stars get expanded to all fields in the table */ |
||||
else if (fi[i]->name[0] == '*') { |
||||
|
||||
char do_all_tables; |
||||
int total_cols, old_size, need, cols; |
||||
|
||||
mylog("expanding field %d\n", i); |
||||
|
||||
total_cols = 0;
|
||||
|
||||
if (fi[i]->ti) /* The star represents only the qualified table */ |
||||
total_cols = QR_get_num_tuples(fi[i]->ti->col_info->result); |
||||
|
||||
else { /* The star represents all tables */ |
||||
|
||||
/* Calculate the total number of columns after expansion */ |
||||
for (k = 0; k < stmt->ntab; k++) { |
||||
total_cols += QR_get_num_tuples(ti[k]->col_info->result); |
||||
} |
||||
} |
||||
total_cols--; /* makes up for the star */ |
||||
|
||||
/* Allocate some more field pointers if necessary */ |
||||
//-------------------------------------------------------------
|
||||
old_size = (stmt->nfld / FLD_INCR * FLD_INCR + FLD_INCR); |
||||
need = total_cols - (old_size - stmt->nfld); |
||||
|
||||
mylog("k=%d, total_cols=%d, old_size=%d, need=%d\n", k,total_cols,old_size,need); |
||||
|
||||
if (need > 0) { |
||||
int new_size = need / FLD_INCR * FLD_INCR + FLD_INCR; |
||||
mylog("need more cols: new_size = %d\n", new_size); |
||||
fi = (FIELD_INFO **) realloc(fi, (old_size + new_size) * sizeof(FIELD_INFO *)); |
||||
if ( ! fi) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
} |
||||
|
||||
//-------------------------------------------------------------
|
||||
// copy any other fields (if there are any) up past the expansion
|
||||
for (j = stmt->nfld - 1; j > i; j--) { |
||||
mylog("copying field %d to %d\n", j, total_cols + j); |
||||
fi[total_cols + j] = fi[j]; |
||||
} |
||||
mylog("done copying fields\n"); |
||||
|
||||
//-------------------------------------------------------------
|
||||
// Set the new number of fields
|
||||
stmt->nfld += total_cols; |
||||
mylog("stmt->nfld now at %d\n", stmt->nfld); |
||||
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// copy the new field info
|
||||
|
||||
|
||||
do_all_tables = (fi[i]->ti ? FALSE : TRUE); |
||||
|
||||
for (k = 0; k < (do_all_tables ? stmt->ntab : 1); k++) { |
||||
|
||||
TABLE_INFO *the_ti = do_all_tables ? ti[k] : fi[i]->ti; |
||||
|
||||
cols = QR_get_num_tuples(the_ti->col_info->result); |
||||
|
||||
for (n = 0; n < cols; n++) { |
||||
mylog("creating field info: n=%d\n", n); |
||||
// skip malloc (already did it for the Star)
|
||||
if (k > 0 || n > 0) { |
||||
mylog("allocating field info at %d\n", n + i); |
||||
fi[n + i] = (FIELD_INFO *) malloc( sizeof(FIELD_INFO)); |
||||
if (fi[n + i] == NULL) { |
||||
stmt->parse_status = STMT_PARSE_FATAL; |
||||
return FALSE; |
||||
} |
||||
} |
||||
/* Initialize the new space (or the * field) */ |
||||
memset(fi[n + i], 0, sizeof(FIELD_INFO)); |
||||
fi[n + i]->ti = the_ti; |
||||
|
||||
mylog("about to copy at %d\n", n + i); |
||||
|
||||
getColInfo(the_ti->col_info, fi[n + i], n); |
||||
|
||||
mylog("done copying\n"); |
||||
} |
||||
|
||||
i += cols; |
||||
mylog("i now at %d\n", i); |
||||
} |
||||
|
||||
//-------------------------------------------------------------
|
||||
} |
||||
|
||||
/* We either know which table the field was in because it was qualified
|
||||
with a table name or alias -OR- there was only 1 table. |
||||
*/ |
||||
else if (fi[i]->ti) { |
||||
|
||||
if ( ! searchColInfo(fi[i]->ti->col_info, fi[i])) |
||||
parse = FALSE; |
||||
|
||||
i++; |
||||
} |
||||
|
||||
/* Don't know the table -- search all tables in "from" list */ |
||||
else { |
||||
parse = FALSE; |
||||
for (k = 0; k < stmt->ntab; k++) { |
||||
if ( searchColInfo(ti[k]->col_info, fi[i])) { |
||||
fi[i]->ti = ti[k]; /* now know the table */ |
||||
parse = TRUE; |
||||
break; |
||||
} |
||||
} |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
|
||||
if ( ! parse) |
||||
stmt->parse_status = STMT_PARSE_INCOMPLETE; |
||||
else |
||||
stmt->parse_status = STMT_PARSE_COMPLETE; |
||||
|
||||
|
||||
mylog("done parse_statement: parse=%d, parse_status=%d\n", parse, stmt->parse_status); |
||||
return parse; |
||||
} |
||||
|
||||
Loading…
Reference in new issue