Logo Search packages:      
Sourcecode: psqlodbc version File versions

parse.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"

#include "multibyte.h"

#define FLD_INCR  32
#define TAB_INCR  8
#define COL_INCR  16

char     *getNextToken(int ccsc, char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);
void        getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k);
char        searchColInfo(COL_INFO *col_info, FIELD_INFO *fi);

Int4 FI_precision(const FIELD_INFO *fi)
{
      if (!fi)    return -1;
      switch (fi->type)
      {
            case PG_TYPE_NUMERIC:
                  return fi->column_size;
            case PG_TYPE_DATETIME:
            case PG_TYPE_TIMESTAMP_NO_TMZONE:
                  return fi->decimal_digits;
      }
      return 0;
}
Int4 FI_scale(const FIELD_INFO *fi)
{
      if (!fi)    return -1;
      switch (fi->type)
      {
            case PG_TYPE_NUMERIC:
                  return fi->decimal_digits;
      }
      return 0;
}

char *
getNextToken(
      int ccsc, /* client encoding */
      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;
      encoded_str encstr;

      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;

      encoded_str_constr(&encstr, ccsc, &s[i]);
      /* get the next token */
      while (s[i] != '\0' && out < smax)
      {
            encoded_nextchar(&encstr);
            if (ENCODE_STATUS(encstr) != 0)
            {
                  token[out++] = s[i++];
                  continue;
            }
            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)
                  {
                        encoded_nextchar(&encstr);
                        if (ENCODE_STATUS(encstr) != 0)
                        {
                              token[out++] = s[i++];
                              continue;
                        }
                        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_backend_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;
}

/*
 *    lower the unquoted name
 */
static
void lower_the_name(char *name, ConnectionClass *conn, BOOL dquote)
{
      if (!dquote)
      {
            char        *ptr;
            encoded_str encstr;
            make_encoded_str(&encstr, conn, name);

            /* lower case table name */
            for (ptr = name; *ptr; ptr++)
            {
                  encoded_nextchar(&encstr);
                  if (ENCODE_STATUS(encstr) == 0)
                        *ptr = tolower((unsigned char) *ptr);
            }
      }
}

char
parse_statement(StatementClass *stmt)
{
      CSTR        func = "parse_statement";
      char        token[256], stoken[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,
                        out_table = TRUE;
      char        in_field = FALSE,
                        in_expr = FALSE,
                        in_func = FALSE,
                        in_dot = FALSE,
                        in_as = FALSE;
      int               j,
                        i,
                        k = 0,
                        n,
                        blevel = 0, old_blevel, subqlevel = 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;

      while (pptr = ptr, (ptr = getNextToken(conn->ccsc, pptr, token, sizeof(token), &delim, &quote, &dquote, &numeric)) != NULL)
      {
            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);

            old_blevel = blevel;
            if (unquoted && blevel == 0)
            {
                  if (in_select)
                  {
                        if (!stricmp(token, "distinct"))
                        {
                              in_distinct = TRUE;
                              updatable = FALSE;

                              mylog("DISTINCT\n");
                              continue;
                        }
                        else if (!stricmp(token, "into"))
                        {
                              in_select = FALSE;
                              mylog("INTO\n");
                              stmt->statement_type = STMT_TYPE_CREATE;
                              stmt->parse_status = STMT_PARSE_FATAL;
                              return FALSE;
                        }
                        else 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 */
                  else if ((!stricmp(token, "where") ||
                         !stricmp(token, "union") ||
                         !stricmp(token, "intersect") ||
                         !stricmp(token, "except") ||
                         !stricmp(token, "order") ||
                         !stricmp(token, "group") ||
                         !stricmp(token, "having")))
                  {
                        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 */
            /* check the change of blevel etc */
            if (unquoted)
            {
                  if (!stricmp(token, "select"))
                  {
                        stoken[0] = '\0';
                        if (0 == blevel)
                        {
                              in_select = TRUE; 
                              mylog("SELECT\n");
                              continue;
                        }
                        else
                        {
                              mylog("SUBSELECT\n");
                              if (0 == subqlevel)
                                    subqlevel = blevel;
                        }
                  }
                  else if (token[0] == '(')
                  {
                        blevel++;
                        mylog("blevel++ = %d\n", blevel);
                        /* aggregate function ? */
                        if (stoken[0] && updatable && 0 == subqlevel)
                        {
                              if (stricmp(stoken, "count") == 0 ||
                                  stricmp(stoken, "sum") == 0 ||
                                  stricmp(stoken, "avg") == 0 ||
                                  stricmp(stoken, "max") == 0 ||
                                  stricmp(stoken, "min") == 0 ||
                                  stricmp(stoken, "variance") == 0 ||
                                  stricmp(stoken, "stddev") == 0)
                                    updatable = FALSE;
                        }
                  }
                  else if (token[0] == ')')
                  {
                        blevel--;
                        mylog("blevel-- = %d\n", blevel);
                        if (blevel < subqlevel)
                              subqlevel = 0;
                  }
                  if (blevel >= old_blevel && ',' != delim)
                        strcpy(stoken, token);
                  else
                        stoken[0] = '\0';
            }
            if (in_select)
            {
                  if (in_expr || in_func)
                  {
                        /* just eat the expression */
                        mylog("in_expr=%d or func=%d\n", in_expr, in_func);

                        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_expr || in_func) && 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 (0 == old_blevel && blevel > 0)
                        {                       /* expression */
                              mylog("got EXPRESSION\n");
                              fi[irdflds->nfields++]->expr = TRUE;
                              in_expr = TRUE;
                              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)
                  {
                        int   ifld = irdflds->nfields - 1;

                        if (fi[ifld]->dot[0])
                        {
                              fi[ifld]->schema = strdup(fi[ifld]->dot);
                        }
                        strcpy(fi[ifld]->dot, fi[ifld]->name);
                        strcpy(fi[ifld]->name, token);

                        if (delim == ',')
                        {
                              mylog("in_dot: got comma\n");
                              in_field = FALSE;
                        }
                        in_dot = 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 (0 == old_blevel && blevel > 0)
                  {
                        in_dot = FALSE;
                        in_func = TRUE;
                        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;
                  }

                  in_dot = FALSE;
                  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 (token[0] == ';')
                  {
                        in_from = FALSE;
                        break;
                  }
                  switch (token[0])
                  {
                        case '\0':
                              continue;
                        case ',':
                              out_table = TRUE; 
                              continue;
                  }
                  if (out_table && !in_table) /* new table */
                  {
                        if (!dquote)
                        {
                              if (token[0] == '(' ||
                                  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]->schema[0] = '\0';
                        ti[stmt->ntab]->alias[0] = '\0';
                        ti[stmt->ntab]->updatable = 1;

                        strcpy(ti[stmt->ntab]->name, token);
                        lower_the_name(ti[stmt->ntab]->name, conn, dquote);
                        mylog("got table = '%s'\n", ti[stmt->ntab]->name);

                        if (delim == ',')
                        {
                              out_table = TRUE;
                              mylog("more than 1 tables\n");
                        }
                        else
                        {
                              out_table = FALSE;
                              in_table = TRUE;
                        }
                        stmt->ntab++;
                        in_dot = FALSE;
                        continue;
                  }

                  if (!dquote && stricmp(token, "JOIN") == 0)
                  {
                        in_table = FALSE;
                        out_table = TRUE;
                        continue;
                  }
                  if (in_table)
                  {
                        if (in_dot)
                        {
                              strcpy(ti[stmt->ntab - 1]->schema, ti[stmt->ntab - 1]->name);
                              strcpy(ti[stmt->ntab - 1]->name, token);
                              lower_the_name(ti[stmt->ntab - 1]->name, conn, dquote);
                              in_dot = FALSE;
                              continue;
                        }
                        if (strcmp(token, ".") == 0)
                        {
                              in_dot = TRUE;
                              continue;
                        }
                        if (dquote || stricmp(token, "as"))
                        {
                              if (stricmp(token, "LEFT") == 0 ||
                                  stricmp(token, "RIGHT") == 0 ||
                                  stricmp(token, "OUTER") == 0 ||
                                  stricmp(token, "FULL") == 0 ||
                                  stricmp(token, "ON") == 0)
                              {
                                    if (!dquote)
                                    {
                                          in_table = FALSE;
                                          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 == ',')
                              {
                                    out_table = TRUE;
                                    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;
            }
            /* field name contains the schema name */
            else if (fi[i]->schema)
            {
                  int   matchidx = -1;

                  for (k = 0; k < stmt->ntab; k++)
                  {
                        if (!stricmp(ti[k]->name, fi[i]->dot))
                        {
                              if (!stricmp(ti[k]->schema, fi[i]->schema))
                              {
                                    fi[i]->ti = ti[k];
                                    break;
                              }
                              else if (!ti[k]->schema[0])
                              {
                                    if (matchidx < 0)
                                          matchidx = k;
                                    else
                                    {
                                          stmt->parse_status = STMT_PARSE_FATAL;
                                          SC_set_error(stmt, STMT_EXEC_ERROR, "duplicated Table name");
                                          stmt->updatable = FALSE;
                                          return FALSE;
                                    }
                              }
                        }
                  }
                  if (matchidx >= 0)
                        fi[i]->ti = ti[matchidx];
            }
            /* 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]->alias, fi[i]->dot))
                        {
                              fi[i]->ti = ti[k];
                              break;
                        }
                        else if (!stricmp(ti[k]->name, 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);
      if (0 == stmt->ntab)
      {
            stmt->parse_status = STMT_PARSE_FATAL;
            return      FALSE;
      }

      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;

            if (conn->schema_support)
            {
                  if (!ti[i]->schema[0])
                  {
                        const char *curschema = CC_get_current_schema(conn);
                        /*
                         * Though current_schema() doesn't have
                         * much sense in PostgreSQL, we first
                         * check the current_schema() when no
                         * explicit schema name was specified.
                         */
                        for (k = 0; k < conn->ntables; k++)
                        {
                              if (!stricmp(conn->col_info[k]->name, ti[i]->name) &&
                                  !stricmp(conn->col_info[k]->schema, curschema))
                              {
                                    mylog("FOUND col_info table='%s' current schema='%s'\n", ti[i]->name, curschema);
                                    found = TRUE;
                                    strcpy(ti[i]->schema, curschema);
                                    break;
                              }
                        }
                        if (!found)
                        {
                              QResultClass      *res;
                              BOOL        tblFound = FALSE;

                              /*
                               * We also have to check as follows.
                               */
                              sprintf(token, "select nspname from pg_namespace n, pg_class c"
                                    " where c.relnamespace=n.oid and c.oid='\"%s\"'::regclass", ti[i]->name);
                              res = CC_send_query(conn, token, NULL, CLEAR_RESULT_ON_ABORT);
                              if (res)
                              {
                                    if (QR_get_num_total_tuples(res) == 1)
                                    {
                                          tblFound = TRUE;
                                          strcpy(ti[i]->schema, QR_get_value_backend_row(res, 0, 0));
                                    }
                                    QR_Destructor(res);
                              }
                              else
                                    CC_abort(conn);
                              if (!tblFound)
                              {
                                    stmt->parse_status = STMT_PARSE_FATAL;
                                    SC_set_error(stmt, STMT_EXEC_ERROR, "Table not found");
                                    stmt->updatable = FALSE;
                                    return FALSE;
                              }
                        }
                  }
                  if (!found && ti[i]->schema[0])
                  {
                        for (k = 0; k < conn->ntables; k++)
                        {
                              if (!stricmp(conn->col_info[k]->name, ti[i]->name) &&
                                  !stricmp(conn->col_info[k]->schema, ti[i]->schema))
                              {
                                    mylog("FOUND col_info table='%s' schema='%s'\n", ti[i]->name, ti[i]->schema);
                                    found = TRUE;
                                    break;
                              }
                        }
                  }
            }
            else
            {
                  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))
                  {
                        SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for columns.");
                        stmt->parse_status = STMT_PARSE_FATAL;
                        return FALSE;
                  }

                  col_stmt = (StatementClass *) hcol_stmt;
                  col_stmt->internal = TRUE;

                  result = PGAPI_Columns(hcol_stmt, "", 0, ti[i]->schema,
                               SQL_NTS, ti[i]->name, SQL_NTS, "", 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
                         */
                        if (ti[i]->schema[0])
                              conn->col_info[conn->ntables]->schema = strdup(ti[i]->schema);
                        else
                              conn->col_info[conn->ntables]->schema = NULL;
                        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
       */
      if (updatable && 1 == stmt->ntab)
            updatable = stmt->ti[0]->updatable;
      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_backend_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_backend_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_backend_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;
}

Generated by  Doxygen 1.6.0   Back to index