mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-09 20:37:29 +08:00
1) Prepare to separate 4 kinds of Descriptor handles. 2) Detect the transaction status more naturally. 3) Improve Parse Statement functionality for the use of updatable cursors. 4) Improve updatable cursors. 5) Implement SQLGetDescField() and improve SQLColAttribute(). 6) etc.
1011 lines
23 KiB
C
1011 lines
23 KiB
C
/*--------
|
|
* 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.
|
|
*--------
|
|
*/
|
|
/* Multibyte support Eiji Tokuya 2001-03-15 */
|
|
|
|
#include "psqlodbc.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "statement.h"
|
|
#include "connection.h"
|
|
#include "qresult.h"
|
|
#include "pgtypes.h"
|
|
#include "pgapifunc.h"
|
|
|
|
#ifdef MULTIBYTE
|
|
#include "multibyte.h"
|
|
#endif
|
|
|
|
#define FLD_INCR 32
|
|
#define TAB_INCR 8
|
|
#define COL_INCR 16
|
|
|
|
#ifdef MULTIBYTE
|
|
char *getNextToken(int ccsc, char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);
|
|
#else
|
|
char *getNextToken(char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);
|
|
#endif /* MULTIBYTE */
|
|
void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k);
|
|
char searchColInfo(COL_INFO *col_info, FIELD_INFO *fi);
|
|
|
|
|
|
char *
|
|
getNextToken(
|
|
#ifdef MULTIBYTE
|
|
int ccsc, /* client encoding */
|
|
#endif /* MULTIBYTE */
|
|
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;
|
|
#ifdef MULTIBYTE
|
|
encoded_str encstr;
|
|
#endif
|
|
|
|
if (smax <= 1)
|
|
return NULL;
|
|
|
|
smax--;
|
|
|
|
/* skip leading delimiters */
|
|
while (isspace((unsigned char) s[i]) || s[i] == ',')
|
|
{
|
|
/* mylog("skipping '%c'\n", s[i]); */
|
|
i++;
|
|
}
|
|
|
|
if (s[i] == '\0')
|
|
{
|
|
token[0] = '\0';
|
|
return NULL;
|
|
}
|
|
|
|
if (quote)
|
|
*quote = FALSE;
|
|
if (dquote)
|
|
*dquote = FALSE;
|
|
if (numeric)
|
|
*numeric = FALSE;
|
|
|
|
#ifdef MULTIBYTE
|
|
encoded_str_constr(&encstr, ccsc, &s[i]);
|
|
#endif
|
|
/* get the next token */
|
|
while (s[i] != '\0' && out < smax)
|
|
{
|
|
#ifdef MULTIBYTE
|
|
encoded_nextchar(&encstr);
|
|
if (ENCODE_STATUS(encstr) != 0)
|
|
{
|
|
token[out++] = s[i++];
|
|
continue;
|
|
}
|
|
#endif
|
|
if (isspace((unsigned char) s[i]) || s[i] == ',')
|
|
break;
|
|
/* 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)
|
|
{
|
|
#ifdef MULTIBYTE
|
|
encoded_nextchar(&encstr);
|
|
if (ENCODE_STATUS(encstr) != 0)
|
|
{
|
|
token[out++] = s[i++];
|
|
continue;
|
|
}
|
|
#endif
|
|
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((unsigned char) s[i]))
|
|
{
|
|
if (numeric)
|
|
*numeric = TRUE;
|
|
token[out++] = s[i++];
|
|
while (isalnum((unsigned char) s[i]) || s[i] == '.')
|
|
token[out++] = s[i++];
|
|
|
|
break;
|
|
}
|
|
|
|
if (ispunct((unsigned char) s[i]) && s[i] != '_')
|
|
{
|
|
mylog("got ispunct: s[%d] = '%c'\n", i, 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((unsigned char) 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((unsigned char) s[i]))
|
|
i++;
|
|
|
|
return &s[i];
|
|
}
|
|
|
|
|
|
#if 0
|
|
QR_set_num_fields(SC_get_Curres(stmt), 14);
|
|
QR_set_field_info(SC_get_Curres(stmt), 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
|
|
QR_set_field_info(SC_get_Curres(stmt), 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
|
|
QR_set_field_info(SC_get_Curres(stmt), 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
|
|
QR_set_field_info(SC_get_Curres(stmt), 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
|
|
QR_set_field_info(SC_get_Curres(stmt), 4, "DATA_TYPE", PG_TYPE_INT2, 2);
|
|
QR_set_field_info(SC_get_Curres(stmt), 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
|
|
QR_set_field_info(SC_get_Curres(stmt), 6, "PRECISION", PG_TYPE_INT4, 4);
|
|
QR_set_field_info(SC_get_Curres(stmt), 7, "LENGTH", PG_TYPE_INT4, 4);
|
|
QR_set_field_info(SC_get_Curres(stmt), 8, "SCALE", PG_TYPE_INT2, 2);
|
|
QR_set_field_info(SC_get_Curres(stmt), 9, "RADIX", PG_TYPE_INT2, 2);
|
|
QR_set_field_info(SC_get_Curres(stmt), 10, "NULLABLE", PG_TYPE_INT2, 2);
|
|
QR_set_field_info(SC_get_Curres(stmt), 11, "REMARKS", PG_TYPE_TEXT, 254);
|
|
/* User defined fields */
|
|
QR_set_field_info(SC_get_Curres(stmt), 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4);
|
|
QR_set_field_info(SC_get_Curres(stmt), 13, "FIELD_TYPE", PG_TYPE_INT4, 4);
|
|
#endif
|
|
|
|
void
|
|
getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k)
|
|
{
|
|
char *str;
|
|
Int2 reserved_cols;
|
|
|
|
#if (ODBCVER >= 0x0300)
|
|
reserved_cols = 18;
|
|
#else
|
|
reserved_cols = 12;
|
|
#endif /* ODBCVER */
|
|
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, (Int2)(reserved_cols + 1)));
|
|
fi->column_size = atoi(QR_get_value_manual(col_info->result, k, 6));
|
|
fi->length = atoi(QR_get_value_manual(col_info->result, k, 7));
|
|
if (str = QR_get_value_manual(col_info->result, k, 8), str)
|
|
fi->decimal_digits = atoi(str);
|
|
else
|
|
fi->decimal_digits = -1;
|
|
fi->nullable = atoi(QR_get_value_manual(col_info->result, k, 10));
|
|
fi->display_size = atoi(QR_get_value_manual(col_info->result, k, reserved_cols));
|
|
}
|
|
|
|
|
|
char
|
|
searchColInfo(COL_INFO *col_info, FIELD_INFO *fi)
|
|
{
|
|
int k,
|
|
cmp;
|
|
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 (fi->dquote)
|
|
cmp = strcmp(col, fi->name);
|
|
else
|
|
cmp = stricmp(col, fi->name);
|
|
if (!cmp)
|
|
{
|
|
if (!fi->dquote)
|
|
strcpy(fi->name, col);
|
|
getColInfo(col_info, fi, k);
|
|
|
|
mylog("PARSE: searchColInfo: \n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
char
|
|
parse_statement(StatementClass *stmt)
|
|
{
|
|
static char *func = "parse_statement";
|
|
char token[256];
|
|
char delim,
|
|
quote,
|
|
dquote,
|
|
numeric,
|
|
unquoted;
|
|
char *ptr,
|
|
*pptr = NULL;
|
|
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 = 0,
|
|
n,
|
|
blevel = 0;
|
|
FIELD_INFO **fi;
|
|
TABLE_INFO **ti;
|
|
char parse;
|
|
ConnectionClass *conn = stmt->hdbc;
|
|
HSTMT hcol_stmt;
|
|
StatementClass *col_stmt;
|
|
IRDFields *irdflds = SC_get_IRD(stmt);
|
|
RETCODE result;
|
|
BOOL updatable = TRUE;
|
|
|
|
mylog("%s: entering...\n", func);
|
|
|
|
ptr = stmt->statement;
|
|
fi = irdflds->fi;
|
|
ti = stmt->ti;
|
|
|
|
irdflds->nfields = 0;
|
|
stmt->ntab = 0;
|
|
stmt->from_pos = -1;
|
|
stmt->where_pos = -1;
|
|
|
|
#ifdef MULTIBYTE
|
|
while (pptr = ptr, (ptr = getNextToken(conn->ccsc, pptr, token, sizeof(token), &delim, "e, &dquote, &numeric)) != NULL)
|
|
#else
|
|
while (pptr = ptr, (ptr = getNextToken(pptr, token, sizeof(token), &delim, "e, &dquote, &numeric)) != NULL)
|
|
#endif
|
|
{
|
|
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 (in_select && unquoted && blevel == 0)
|
|
{
|
|
if (!stricmp(token, "distinct"))
|
|
{
|
|
in_distinct = TRUE;
|
|
updatable = FALSE;
|
|
|
|
mylog("DISTINCT\n");
|
|
continue;
|
|
}
|
|
if (!stricmp(token, "into"))
|
|
{
|
|
in_select = FALSE;
|
|
mylog("INTO\n");
|
|
stmt->statement_type = STMT_TYPE_CREATE;
|
|
stmt->parse_status = STMT_PARSE_FATAL;
|
|
return FALSE;
|
|
}
|
|
if (!stricmp(token, "from"))
|
|
{
|
|
in_select = FALSE;
|
|
in_from = TRUE;
|
|
if (stmt->from_pos < 0 &&
|
|
(!strnicmp(pptr, "from", 4)))
|
|
{
|
|
mylog("First ");
|
|
stmt->from_pos = pptr - stmt->statement;
|
|
}
|
|
|
|
mylog("FROM\n");
|
|
continue;
|
|
}
|
|
} /* in_select && unquoted && blevel == 0 */
|
|
if (unquoted && blevel == 0)
|
|
{
|
|
if ((!stricmp(token, "where") ||
|
|
!stricmp(token, "union") ||
|
|
!stricmp(token, "intersect") ||
|
|
!stricmp(token, "except") ||
|
|
!stricmp(token, "order") ||
|
|
!stricmp(token, "group") ||
|
|
!stricmp(token, "having")))
|
|
{
|
|
in_select = FALSE;
|
|
in_from = FALSE;
|
|
in_where = TRUE;
|
|
|
|
if (stmt->where_pos < 0)
|
|
stmt->where_pos = pptr - stmt->statement;
|
|
mylog("%s...\n", token);
|
|
if (stricmp(token, "where") &&
|
|
stricmp(token, "order"))
|
|
{
|
|
updatable = FALSE;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
} /* unquoted && blevel == 0 */
|
|
if (in_select && (in_expr || in_func))
|
|
{
|
|
/* just eat the expression */
|
|
mylog("in_expr=%d or func=%d\n", in_expr, in_func);
|
|
|
|
if (unquoted)
|
|
{
|
|
if (token[0] == '(')
|
|
{
|
|
blevel++;
|
|
mylog("blevel++ = %d\n", blevel);
|
|
}
|
|
else if (token[0] == ')')
|
|
{
|
|
blevel--;
|
|
mylog("blevel-- = %d\n", blevel);
|
|
}
|
|
}
|
|
if (blevel == 0)
|
|
{
|
|
if (delim == ',')
|
|
{
|
|
mylog("**** Got comma in_expr/func\n");
|
|
in_func = FALSE;
|
|
in_expr = FALSE;
|
|
in_field = FALSE;
|
|
}
|
|
else if (unquoted && !stricmp(token, "as"))
|
|
{
|
|
mylog("got AS in_expr\n");
|
|
in_func = FALSE;
|
|
in_expr = FALSE;
|
|
in_as = TRUE;
|
|
in_field = TRUE;
|
|
}
|
|
}
|
|
continue;
|
|
} /* in_select && (in_expr || in_func) */
|
|
|
|
if (unquoted && !stricmp(token, "select"))
|
|
{
|
|
in_select = TRUE;
|
|
|
|
mylog("SELECT\n");
|
|
continue;
|
|
}
|
|
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;
|
|
} /* in_distinct */
|
|
|
|
if (!in_field)
|
|
{
|
|
if (!token[0])
|
|
continue;
|
|
|
|
if (!(irdflds->nfields % FLD_INCR))
|
|
{
|
|
mylog("reallocing at nfld=%d\n", irdflds->nfields);
|
|
fi = (FIELD_INFO **) realloc(fi, (irdflds->nfields + FLD_INCR) * sizeof(FIELD_INFO *));
|
|
if (!fi)
|
|
{
|
|
stmt->parse_status = STMT_PARSE_FATAL;
|
|
return FALSE;
|
|
}
|
|
irdflds->fi = fi;
|
|
}
|
|
|
|
fi[irdflds->nfields] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
|
|
if (fi[irdflds->nfields] == NULL)
|
|
{
|
|
stmt->parse_status = STMT_PARSE_FATAL;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Initialize the field info */
|
|
memset(fi[irdflds->nfields], 0, sizeof(FIELD_INFO));
|
|
|
|
/* double quotes are for qualifiers */
|
|
if (dquote)
|
|
fi[irdflds->nfields]->dquote = TRUE;
|
|
|
|
if (quote)
|
|
{
|
|
fi[irdflds->nfields]->quote = TRUE;
|
|
fi[irdflds->nfields]->column_size = strlen(token);
|
|
}
|
|
else if (numeric)
|
|
{
|
|
mylog("**** got numeric: nfld = %d\n", irdflds->nfields);
|
|
fi[irdflds->nfields]->numeric = TRUE;
|
|
}
|
|
else if (token[0] == '(')
|
|
{ /* expression */
|
|
mylog("got EXPRESSION\n");
|
|
fi[irdflds->nfields++]->expr = TRUE;
|
|
in_expr = TRUE;
|
|
blevel = 1;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
strcpy(fi[irdflds->nfields]->name, token);
|
|
fi[irdflds->nfields]->dot[0] = '\0';
|
|
}
|
|
mylog("got field='%s', dot='%s'\n", fi[irdflds->nfields]->name, fi[irdflds->nfields]->dot);
|
|
|
|
if (delim == ',')
|
|
mylog("comma (1)\n");
|
|
else
|
|
in_field = TRUE;
|
|
irdflds->nfields++;
|
|
continue;
|
|
} /* !in_field */
|
|
|
|
/*
|
|
* We are in a field now
|
|
*/
|
|
if (in_dot)
|
|
{
|
|
irdflds->nfields--;
|
|
strcpy(fi[irdflds->nfields]->dot, fi[irdflds->nfields]->name);
|
|
strcpy(fi[irdflds->nfields]->name, token);
|
|
irdflds->nfields++;
|
|
in_dot = FALSE;
|
|
|
|
if (delim == ',')
|
|
{
|
|
mylog("in_dot: got comma\n");
|
|
in_field = FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (in_as)
|
|
{
|
|
irdflds->nfields--;
|
|
strcpy(fi[irdflds->nfields]->alias, token);
|
|
mylog("alias for field '%s' is '%s'\n", fi[irdflds->nfields]->name, fi[irdflds->nfields]->alias);
|
|
in_as = FALSE;
|
|
in_field = FALSE;
|
|
|
|
irdflds->nfields++;
|
|
|
|
if (delim == ',')
|
|
mylog("comma(2)\n");
|
|
continue;
|
|
}
|
|
|
|
/* Function */
|
|
if (token[0] == '(')
|
|
{
|
|
in_func = TRUE;
|
|
blevel = 1;
|
|
fi[irdflds->nfields - 1]->func = TRUE;
|
|
|
|
/*
|
|
* name will have the function name -- maybe useful some
|
|
* day
|
|
*/
|
|
mylog("**** got function = '%s'\n", fi[irdflds->nfields - 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, it's probably an expression */
|
|
in_expr = TRUE;
|
|
fi[irdflds->nfields - 1]->expr = TRUE;
|
|
fi[irdflds->nfields - 1]->name[0] = '\0';
|
|
fi[irdflds->nfields - 1]->column_size = 0;
|
|
mylog("*** setting expression\n");
|
|
} /* in_select end */
|
|
|
|
if (in_from)
|
|
{
|
|
if (!in_table)
|
|
{
|
|
if (!token[0])
|
|
continue;
|
|
if (token[0] == ';')
|
|
break;
|
|
|
|
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);
|
|
if (!dquote)
|
|
{
|
|
char *ptr;
|
|
#ifdef MULTIBYTE
|
|
encoded_str encstr;
|
|
make_encoded_str(&encstr, conn, ti[stmt->ntab]->name);
|
|
#endif /* MULTIBYTE */
|
|
|
|
/* lower case table name */
|
|
for (ptr = ti[stmt->ntab]->name; *ptr; ptr++)
|
|
{
|
|
#ifdef MULTIBYTE
|
|
encoded_nextchar(&encstr);
|
|
if (ENCODE_STATUS(encstr) != 0)
|
|
ptr++;
|
|
else
|
|
#endif /* MULTIBYTE */
|
|
*ptr = tolower((unsigned char) *ptr);
|
|
}
|
|
}
|
|
mylog("got table = '%s'\n", ti[stmt->ntab]->name);
|
|
|
|
if (delim == ',')
|
|
mylog("more than 1 tables\n");
|
|
else
|
|
in_table = TRUE;
|
|
stmt->ntab++;
|
|
continue;
|
|
}
|
|
|
|
if (token[0] == ';')
|
|
break;
|
|
if (stricmp(token, "as"))
|
|
{
|
|
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");
|
|
}
|
|
} /* in_from */
|
|
}
|
|
|
|
/*
|
|
* Resolve any possible field names with tables
|
|
*/
|
|
|
|
parse = TRUE;
|
|
|
|
/* Resolve field names with tables */
|
|
for (i = 0; i < (int) irdflds->nfields; 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]->column_size = 0; the
|
|
* following may be better
|
|
*/
|
|
fi[i]->type = PG_TYPE_UNKNOWN;
|
|
if (fi[i]->column_size == 0)
|
|
{
|
|
fi[i]->type = PG_TYPE_VARCHAR;
|
|
fi[i]->column_size = 254;
|
|
}
|
|
fi[i]->length = fi[i]->column_size;
|
|
continue;
|
|
}
|
|
/* it's 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", irdflds->nfields, stmt->ntab);
|
|
|
|
for (i = 0; i < (int) irdflds->nfields; 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 */
|
|
if (stmt->ntab > 1)
|
|
updatable = FALSE;
|
|
else if (stmt->from_pos < 0)
|
|
updatable = FALSE;
|
|
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 PG_Columns for table[%d]='%s'\n", i, ti[i]->name);
|
|
|
|
result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
|
|
if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
|
|
{
|
|
stmt->errormsg = "PGAPI_AllocStmt 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 = PGAPI_Columns(hcol_stmt, "", 0, "", 0,
|
|
ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0, PODBC_NOT_SEARCH_PATTERN);
|
|
|
|
mylog(" Past PG_Columns\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 = SC_get_Curres(col_stmt);
|
|
|
|
/*
|
|
* The connection will now free the result structures, so
|
|
* make sure that the statement doesn't free it
|
|
*/
|
|
SC_set_Result(col_stmt, NULL);
|
|
|
|
conn->ntables++;
|
|
|
|
PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
|
|
mylog("Created col_info table='%s', ntables=%d\n", ti[i]->name, conn->ntables);
|
|
}
|
|
else
|
|
{
|
|
PGAPI_FreeStmt(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 PG_Columns\n");
|
|
|
|
/*
|
|
* Now resolve the fields to point to column info
|
|
*/
|
|
for (i = 0; i < (int) irdflds->nfields;)
|
|
{
|
|
fi[i]->updatable = updatable;
|
|
/* Dont worry about functions or quotes */
|
|
if (fi[i]->func || fi[i]->quote || fi[i]->numeric)
|
|
{
|
|
fi[i]->updatable = FALSE;
|
|
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_alloc,
|
|
new_size,
|
|
cols;
|
|
int increased_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);
|
|
}
|
|
increased_cols = total_cols - 1;
|
|
|
|
/* Allocate some more field pointers if necessary */
|
|
old_alloc = ((irdflds->nfields - 1) / FLD_INCR + 1) * FLD_INCR;
|
|
new_size = irdflds->nfields + increased_cols;
|
|
|
|
mylog("k=%d, increased_cols=%d, old_alloc=%d, new_size=%d\n", k, increased_cols, old_alloc, new_size);
|
|
|
|
if (new_size > old_alloc)
|
|
{
|
|
int new_alloc = ((new_size / FLD_INCR) + 1) * FLD_INCR;
|
|
|
|
mylog("need more cols: new_alloc = %d\n", new_alloc);
|
|
fi = (FIELD_INFO **) realloc(fi, new_alloc * sizeof(FIELD_INFO *));
|
|
if (!fi)
|
|
{
|
|
stmt->parse_status = STMT_PARSE_FATAL;
|
|
return FALSE;
|
|
}
|
|
irdflds->fi = fi;
|
|
}
|
|
|
|
/*
|
|
* copy any other fields (if there are any) up past the
|
|
* expansion
|
|
*/
|
|
for (j = irdflds->nfields - 1; j > i; j--)
|
|
{
|
|
mylog("copying field %d to %d\n", j, increased_cols + j);
|
|
fi[increased_cols + j] = fi[j];
|
|
}
|
|
mylog("done copying fields\n");
|
|
|
|
/* Set the new number of fields */
|
|
irdflds->nfields += increased_cols;
|
|
mylog("irdflds->nfields now at %d\n", irdflds->nfields);
|
|
|
|
|
|
/* 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);
|
|
fi[n + i]->updatable = updatable;
|
|
|
|
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;
|
|
fi[i]->updatable = FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
/* Don't know the table -- search all tables in "from" list */
|
|
else
|
|
{
|
|
for (k = 0; k < stmt->ntab; k++)
|
|
{
|
|
if (searchColInfo(ti[k]->col_info, fi[i]))
|
|
{
|
|
fi[i]->ti = ti[k]; /* now know the table */
|
|
break;
|
|
}
|
|
}
|
|
if (k >= stmt->ntab)
|
|
{
|
|
parse = FALSE;
|
|
fi[i]->updatable = FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (!parse)
|
|
stmt->parse_status = STMT_PARSE_INCOMPLETE;
|
|
else
|
|
stmt->parse_status = STMT_PARSE_COMPLETE;
|
|
|
|
stmt->updatable = updatable;
|
|
mylog("done parse_statement: parse=%d, parse_status=%d\n", parse, stmt->parse_status);
|
|
return parse;
|
|
}
|