1 /*--------
2  * Module:			parse.c
3  *
4  * Description:		This module contains routines related to parsing SQL
5  *					statements.  This can be useful for two reasons:
6  *
7  *					1. So the query does not actually have to be executed
8  *					to return data about it
9  *
10  *					2. To be able to return information about precision,
11  *					nullability, aliases, etc. in the functions
12  *					SQLDescribeCol and SQLColAttributes.  Currently,
13  *					Postgres doesn't return any information about
14  *					these things in a query.
15  *
16  * Classes:			none
17  *
18  * API functions:	none
19  *
20  * Comments:		See "readme.txt" for copyright and license information.
21  *--------
22  */
23 /* Multibyte support	Eiji Tokuya 2001-03-15 */
24 
25 #include "psqlodbc.h"
26 
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 
31 #include "statement.h"
32 #include "connection.h"
33 #include "qresult.h"
34 #include "pgtypes.h"
35 #include "pgapifunc.h"
36 #include "catfunc.h"
37 
38 #include "multibyte.h"
39 #include "misc.h"
40 
41 #define FLD_INCR	32
42 #define TAB_INCR	8
43 #define COLI_INCR	16
44 #define COLI_RECYCLE	128
45 
46 static const char *getNextToken(int ccsc, char escape_in_literal, const char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);
47 static	void	getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k);
48 static	char	searchColInfo(COL_INFO *col_info, FIELD_INFO *fi);
49 static	BOOL	getColumnsInfo(ConnectionClass *, TABLE_INFO *, OID, StatementClass *);
50 
FI_precision(const FIELD_INFO * fi)51 Int4 FI_precision(const FIELD_INFO *fi)
52 {
53 	OID	ftype;
54 
55 	if (!fi)	return -1;
56 	ftype = FI_type(fi);
57 	switch (ftype)
58 	{
59 		case PG_TYPE_NUMERIC:
60 			return fi->column_size;
61 		case PG_TYPE_DATETIME:
62 		case PG_TYPE_TIMESTAMP_NO_TMZONE:
63 			return fi->decimal_digits;
64 	}
65 	return 0;
66 }
FI_scale(const FIELD_INFO * fi)67 Int4 FI_scale(const FIELD_INFO *fi)
68 {
69 	OID	ftype;
70 
71 	if (!fi)	return -1;
72 	ftype = FI_type(fi);
73 	switch (ftype)
74 	{
75 		case PG_TYPE_NUMERIC:
76 			return fi->decimal_digits;
77 	}
78 	return 0;
79 }
80 
81 static const char *
getNextToken(int ccsc,char escape_ch,const char * s,char * token,int smax,char * delim,char * quote,char * dquote,char * numeric)82 getNextToken(
83 	int ccsc, /* client encoding */
84 	char escape_ch,
85 	const char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric)
86 {
87 	size_t		out = 0;
88 	size_t		taglen;
89 	char		in_quote, in_dollar_quote, in_escape;
90 	const	UCHAR	*tag, *tagend;
91 	encoded_str	encstr;
92 	char		escape_in_literal;
93 	const UCHAR *tstr = (const UCHAR *) s;
94 	UCHAR	tchar, qc;
95 
96 	if (smax <= 1)
97 		return NULL;
98 
99 	smax--;
100 
101 	/* skip leading delimiters */
102 	while (isspace(*tstr) || *tstr == ',')
103 	{
104 		/* MYLOG(0, "skipping '%c'\n", *tstr); */
105 		tstr++;
106 	}
107 
108 	if (*tstr == '\0')
109 	{
110 		token[0] = '\0';
111 		return NULL;
112 	}
113 
114 	if (quote)
115 		*quote = FALSE;
116 	if (dquote)
117 		*dquote = FALSE;
118 	if (numeric)
119 		*numeric = FALSE;
120 
121 	encoded_str_constr(&encstr, ccsc, (const char *) tstr);
122 	/* get the next token */
123 	for (tchar = encoded_nextchar(&encstr); tchar && out < smax; tstr++, tchar = encoded_nextchar(&encstr))
124 	{
125 		if (MBCS_NON_ASCII(encstr))
126 		{
127 			token[out++] = tchar;
128 			continue;
129 		}
130 		if (isspace(tchar) || tchar == ',')
131 			break;
132 		/* Handle quoted stuff */
133 		in_quote = in_dollar_quote = FALSE;
134 		taglen = 0;
135 		tag = NULL;
136 		escape_in_literal = '\0';
137 		if (out == 0)
138 		{
139 			qc = tchar;
140 			if (qc == DOLLAR_QUOTE)
141 			{
142 				in_quote = in_dollar_quote = TRUE;
143 				tag = tstr;
144 				taglen = 1;
145 				if (tagend = (const UCHAR *) strchr((const char *) tstr + 1, DOLLAR_QUOTE), NULL != tagend)
146 					taglen = tagend - tstr + 1;
147 				tstr += (taglen - 1);
148 				encoded_position_shift(&encstr, taglen - 1);
149 				if (quote)
150 					*quote = TRUE;
151 			}
152 			else if (qc == LITERAL_QUOTE)
153 			{
154 				in_quote = TRUE;
155 				if (quote)
156 					*quote = TRUE;
157 				escape_in_literal = escape_ch;
158 				if (!escape_in_literal)
159 				{
160 					if (LITERAL_EXT == tstr[-1])
161 						escape_in_literal = ESCAPE_IN_LITERAL;
162 				}
163 			}
164 			else if (qc == IDENTIFIER_QUOTE)
165 			{
166 				in_quote = TRUE;
167 				if (dquote)
168 					*dquote = TRUE;
169 			}
170 		} /* out == 0 */
171 		if (in_quote) /* dquote, dollar_quote */
172 		{
173 			in_escape = FALSE;
174 			for (tstr++, tchar = encoded_nextchar(&encstr); tchar != '\0' && out != smax; tstr++, tchar = encoded_nextchar(&encstr))
175 			{
176 				if (MBCS_NON_ASCII(encstr))
177 				{
178 					token[out++] = tchar;
179 					continue;
180 				}
181 				if (in_escape)
182 					in_escape = FALSE;
183 				else if (tchar == qc)
184 				{
185 					if (!in_dollar_quote)
186 					{
187 						/*
188 						 * Peek at the next byte to see if this is a '' or
189 						 * "", i.e a quote character that has been escaped
190 						 * by doubling it.
191 						 */
192 						if (tstr[1] == qc)
193 						{
194 							tstr++;
195 							tchar = encoded_nextchar(&encstr);
196 						}
197 						else
198 							break;
199 					}
200 					else if (strncmp((const char *) tstr, (const char *) tag, taglen) == 0)
201 					{
202 						tstr += (taglen - 1);
203 						tchar = encoded_position_shift(&encstr, taglen - 1);
204 						break;
205 					}
206 					token[out++] = tchar;
207 				}
208 				else if (LITERAL_QUOTE == qc && tchar == escape_in_literal)
209 				{
210 					in_escape = TRUE;
211 				}
212 				else
213 				{
214 					token[out++] = tchar;
215 				}
216 			} /* for */
217 			if (tchar == qc)
218 				tstr++;
219 			break;
220 		} /* in_quote */
221 
222 		/* Check for numeric literals */
223 		if (out == 0 && isdigit(tchar))
224 		{
225 			if (numeric)
226 				*numeric = TRUE;
227 			token[out++] = tchar;
228 			tstr++;
229 			while ((isalnum(*tstr) || *tstr == '.') && out < smax)
230 			{
231 				token[out++] = *tstr;
232 				tstr++;
233 			}
234 
235 			break;
236 		}
237 
238 		if (ispunct(tchar) && tchar != '_')
239 		{
240 			MYLOG(0, "got ispunct: s[] = '%c'\n", tchar);
241 
242 			if (out == 0)
243 			{
244 				token[out++] = tchar;
245 				tstr++;
246 			}
247 			break;
248 		}
249 
250 		if (out < smax)
251 			token[out++] = tchar;
252 	} /* for */
253 
254 	/* MYLOG(0, "done -- s[] = '%c'\n", *tstr); */
255 
256 	token[out] = '\0';
257 
258 	/* find the delimiter  */
259 	while (isspace(*tstr))
260 		tstr++;
261 
262 	/* return the most priority delimiter */
263 	if (*tstr == ',')
264 	{
265 		if (delim)
266 			*delim = *tstr;
267 	}
268 	else if (*tstr == '\0')
269 	{
270 		if (delim)
271 			*delim = '\0';
272 	}
273 	else
274 	{
275 		if (delim)
276 			*delim = ' ';
277 	}
278 
279 	/* skip trailing blanks  */
280 	while (isspace(*tstr))
281 		tstr++;
282 
283 	return (const char *) tstr;
284 }
285 
286 static void
getColInfo(COL_INFO * col_info,FIELD_INFO * fi,int k)287 getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k)
288 {
289 	char	   *str;
290 
291 MYLOG(DETAIL_LOG_LEVEL, "entering non-manual result\n");
292 	fi->dquote = TRUE;
293 	STR_TO_NAME(fi->column_name, QR_get_value_backend_text(col_info->result, k, COLUMNS_COLUMN_NAME));
294 
295 	fi->columntype = (OID) QR_get_value_backend_int(col_info->result, k, COLUMNS_FIELD_TYPE, NULL);
296 	fi->column_size = QR_get_value_backend_int(col_info->result, k, COLUMNS_PRECISION, NULL);
297 	fi->length = QR_get_value_backend_int(col_info->result, k, COLUMNS_LENGTH, NULL);
298 	if (str = QR_get_value_backend_text(col_info->result, k, COLUMNS_SCALE), str)
299 		fi->decimal_digits = atoi(str);
300 	else
301 		fi->decimal_digits = -1;
302 	fi->nullable = QR_get_value_backend_int(col_info->result, k, COLUMNS_NULLABLE, NULL);
303 	fi->display_size = QR_get_value_backend_int(col_info->result, k, COLUMNS_DISPLAY_SIZE, NULL);
304 	fi->auto_increment = QR_get_value_backend_int(col_info->result, k, COLUMNS_AUTO_INCREMENT, NULL);
305 }
306 
307 
308 static char
searchColInfo(COL_INFO * col_info,FIELD_INFO * fi)309 searchColInfo(COL_INFO *col_info, FIELD_INFO *fi)
310 {
311 	int			k,
312 				cmp, attnum, atttypmod;
313 	OID			basetype;
314 	const char	   *col;
315 
316 MYLOG(DETAIL_LOG_LEVEL, "entering num_cols=" FORMAT_ULEN " col=%s\n", QR_get_num_cached_tuples(col_info->result), PRINT_NAME(fi->column_name));
317 	if (fi->attnum < 0)
318 		return FALSE;
319 	for (k = 0; k < QR_get_num_cached_tuples(col_info->result); k++)
320 	{
321 		if (fi->attnum > 0)
322 		{
323 			attnum = QR_get_value_backend_int(col_info->result, k, COLUMNS_PHYSICAL_NUMBER, NULL);
324 			if (basetype = (OID) strtoul(QR_get_value_backend_text(col_info->result, k, COLUMNS_BASE_TYPEID), NULL, 10), 0 == basetype)
325 				basetype = (OID) strtoul(QR_get_value_backend_text(col_info->result, k, COLUMNS_FIELD_TYPE), NULL, 10);
326 			atttypmod = QR_get_value_backend_int(col_info->result, k, COLUMNS_ATTTYPMOD, NULL);
327 MYLOG(DETAIL_LOG_LEVEL, "%d attnum=%d\n", k, attnum);
328 			if (attnum == fi->attnum &&
329 			    basetype == fi->basetype &&
330 			    atttypmod == fi->typmod)
331 			{
332 				getColInfo(col_info, fi, k);
333 				MYLOG(0, "PARSE: searchColInfo by attnum=%d\n", attnum);
334 				return TRUE;
335 			}
336 		}
337 		else if (NAME_IS_VALID(fi->column_name))
338 		{
339 			col = QR_get_value_backend_text(col_info->result, k, COLUMNS_COLUMN_NAME);
340 MYLOG(DETAIL_LOG_LEVEL, "%d col=%s\n", k, col);
341 			if (fi->dquote)
342 				cmp = strcmp(col, GET_NAME(fi->column_name));
343 			else
344 				cmp = stricmp(col, GET_NAME(fi->column_name));
345 			if (!cmp)
346 			{
347 				if (!fi->dquote)
348 					STR_TO_NAME(fi->column_name, col);
349 				getColInfo(col_info, fi, k);
350 
351 				MYLOG(0, "PARSE: \n");
352 				return TRUE;
353 			}
354 		}
355 	}
356 
357 	return FALSE;
358 }
359 
360 /*
361  *	lower the unquoted name
362  */
363 static
lower_the_name(char * name,ConnectionClass * conn,BOOL dquote)364 void lower_the_name(char *name, ConnectionClass *conn, BOOL dquote)
365 {
366 	if (!dquote)
367 	{
368 		char		*ptr;
369 		encoded_str	encstr;
370 		make_encoded_str(&encstr, conn, name);
371 
372 		/* lower case table name */
373 		for (ptr = name; *ptr; ptr++)
374 		{
375 			encoded_nextchar(&encstr);
376 			if (!MBCS_NON_ASCII(encstr))
377 				*ptr = tolower((UCHAR) *ptr);
378 		}
379 	}
380 }
381 
382 /*
383  *	Check relhasoids(before PG12), relhssubclass and get some relevant information.
384  */
CheckPgClassInfo(StatementClass * stmt)385 BOOL CheckPgClassInfo(StatementClass *stmt)
386 {
387 	const COL_INFO *coli;
388 	int	table_info;
389 	TABLE_INFO	*ti;
390 	BOOL	hasoids = FALSE, hassubclass =FALSE, keyFound = FALSE;
391 
392 MYLOG(0, "Entering\n");
393 	if (0 != SC_checked_hasoids(stmt))
394 		return TRUE;
395 	if (!stmt->ti || !stmt->ti[0])
396 		return FALSE;
397 	ti = stmt->ti[0];
398 	MYLOG(DETAIL_LOG_LEVEL, "ti->col_info=%p\n", ti->col_info);
399 	if (TI_checked_hasoids(ti))
400 		;
401 	else if (coli = ti->col_info, NULL != coli)
402 	{
403 		table_info = coli->table_info;
404 		if (0 == (table_info & TBINFO_HASSUBCLASS))
405 		{
406 			TI_set_has_no_subclass(ti);
407 		}
408 		else
409 		{
410 			hassubclass = TRUE;
411 			TI_set_hassubclass(ti);
412 			STR_TO_NAME(ti->bestitem, TABLEOID_NAME);
413 			STRX_TO_NAME(ti->bestqual, "\"" TABLEOID_NAME "\" = %u");
414 		}
415 		if (!hassubclass)
416 		{
417 			if (0 == (table_info & TBINFO_HASOIDS))
418 			{
419 				TI_set_has_no_oids(ti);
420 			}
421 			else
422 			{
423 				hasoids = TRUE;
424 				TI_set_hasoids(ti);
425 				STR_TO_NAME(ti->bestitem, OID_NAME);
426 				STRX_TO_NAME(ti->bestqual, "\"" OID_NAME "\" = %u");
427 			}
428 		}
429 		ti->table_oid = coli->table_oid;
430 		if (!hasoids && !hassubclass)
431 		{
432 			QResultClass	*res = coli->result;
433 			int	num_tuples = res ? QR_get_num_cached_tuples(res) : -1;
434 
435 			if (num_tuples > 0)
436 			{
437 				int	i;
438 
439 				for (i = 0; i < num_tuples; i++)
440 				{
441 					if (QR_get_value_backend_int(res, i, COLUMNS_AUTO_INCREMENT, NULL) != 0&&
442 					    QR_get_value_backend_int(res, i, COLUMNS_FIELD_TYPE, NULL) == PG_TYPE_INT4)
443 					{
444 						char		query[512];
445 
446 						STR_TO_NAME(ti->bestitem, QR_get_value_backend_text(res, i, COLUMNS_COLUMN_NAME));
447 						SPRINTF_FIXED(query, "\"%s\" = %%d", SAFE_NAME(ti->bestitem));
448 						STRX_TO_NAME(ti->bestqual, query);
449 						break;
450 					}
451 				}
452 			}
453 		}
454 		TI_set_hasoids_checked(ti);
455 	}
456 	else
457 		return FALSE;
458 
459 	stmt->num_key_fields = PG_NUM_NORMAL_KEYS;
460 	if (TI_has_subclass(ti))
461 		keyFound = FALSE;
462 	else if (TI_has_oids(ti))
463 		keyFound = TRUE;
464 	else if (NAME_IS_NULL(ti->bestqual))
465 	{
466 		keyFound = TRUE;
467 		stmt->num_key_fields--;
468 	}
469 	else
470 		keyFound = TRUE;
471 	MYLOG(DETAIL_LOG_LEVEL, "subclass=%d oids=%d bestqual=%s keyFound=%d num_key_fields=%d\n", TI_has_subclass(ti), TI_has_oids(ti), PRINT_NAME(ti->bestqual), keyFound, stmt->num_key_fields);
472 
473 	SC_set_checked_hasoids(stmt, keyFound);
474 
475 	return TRUE;
476 }
477 
increaseNtab(StatementClass * stmt,const char * func)478 static BOOL increaseNtab(StatementClass *stmt, const char *func)
479 {
480 	TABLE_INFO	**ti = stmt->ti, *wti;
481 
482 	if (!(stmt->ntab % TAB_INCR))
483 	{
484 		SC_REALLOC_return_with_error(ti, TABLE_INFO *, (stmt->ntab + TAB_INCR) * sizeof(TABLE_INFO *), stmt, "PGAPI_AllocStmt failed in parse_statement for TABLE_INFO", FALSE);
485 		stmt->ti = ti;
486 	}
487 	wti = ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO));
488 	if (wti == NULL)
489 	{
490 		SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for TABLE_INFO(2).", func);
491 		return FALSE;
492 	}
493 
494 	TI_Constructor(wti, SC_get_conn(stmt));
495 	stmt->ntab++;
496 	return TRUE;
497 }
498 
setNumFields(IRDFields * irdflds,size_t numFields)499 static void setNumFields(IRDFields *irdflds, size_t numFields)
500 {
501 	FIELD_INFO	**fi = irdflds->fi;
502 	size_t		nfields = irdflds->nfields;
503 
504 	if (numFields < nfields)
505 	{
506 		int	i;
507 
508 		for (i = (int) numFields; i < (int) nfields; i++)
509 		{
510 			if (fi[i])
511 				fi[i]->flag = 0;
512 		}
513 	}
514 	irdflds->nfields = (UInt4) numFields;
515 }
516 
SC_initialize_cols_info(StatementClass * stmt,BOOL DCdestroy,BOOL parseReset)517 void SC_initialize_cols_info(StatementClass *stmt, BOOL DCdestroy, BOOL parseReset)
518 {
519 	IRDFields	*irdflds = SC_get_IRDF(stmt);
520 
521 	/* Free the parsed table information */
522 	if (stmt->ti)
523 	{
524 		TI_Destructor(stmt->ti, stmt->ntab);
525 		free(stmt->ti);
526 		stmt->ti = NULL;
527 	}
528 	stmt->ntab = 0;
529 	if (DCdestroy) /* Free the parsed field information */
530 		DC_Destructor((DescriptorClass *) SC_get_IRD(stmt));
531 	else
532 		setNumFields(irdflds, 0);
533 	if (parseReset)
534 	{
535 		stmt->parse_status = STMT_PARSE_NONE;
536 		SC_reset_updatable(stmt);
537 	}
538 }
539 
allocateFields(IRDFields * irdflds,size_t sizeRequested)540 static BOOL allocateFields(IRDFields *irdflds, size_t sizeRequested)
541 {
542 	FIELD_INFO	**fi = irdflds->fi;
543 	size_t		alloc_size, incr_size;
544 
545 	if (sizeRequested <= irdflds->allocated)
546 		return TRUE;
547 	alloc_size = (0 != irdflds->allocated ? irdflds->allocated : FLD_INCR);
548 	for (; alloc_size < sizeRequested; alloc_size *= 2)
549 			;
550 	incr_size = sizeof(FIELD_INFO *) * (alloc_size - irdflds->allocated);
551 
552 	fi = (FIELD_INFO **) realloc(fi, alloc_size * sizeof(FIELD_INFO *));
553 	if (!fi)
554 	{
555 		irdflds->fi = NULL;
556 		irdflds->allocated = irdflds->nfields = 0;
557 		return FALSE;
558 	}
559 	memset(&fi[irdflds->allocated], 0, incr_size);
560 	irdflds->fi = fi;
561 	irdflds->allocated = (SQLSMALLINT) alloc_size;
562 
563 	return TRUE;
564 }
565 
566 /*
567  *	This function may not be called but when it is called ...
568  */
xxxxx(StatementClass * stmt,FIELD_INFO * fi,QResultClass * res,int i)569 static void xxxxx(StatementClass *stmt, FIELD_INFO *fi, QResultClass *res, int i)
570 {
571 	STR_TO_NAME(fi->column_alias, QR_get_fieldname(res, i));
572 	fi->basetype = QR_get_field_type(res, i);
573 	if (0 == fi->columntype)
574 		fi->columntype = fi->basetype;
575 	if (fi->attnum < 0)
576 	{
577 		fi->nullable = FALSE;
578 		fi->updatable = FALSE;
579 	}
580 	else if (fi->attnum > 0)
581 	{
582 		int	unknowns_as = 0;
583 		int	type = pg_true_type(SC_get_conn(stmt), fi->columntype, fi->basetype);
584 
585 		fi->nullable = TRUE;	/* probably ? */
586 		fi->column_size = pgtype_column_size(stmt, type, i, unknowns_as);
587 		fi->length = pgtype_buffer_length(stmt, type, i, unknowns_as);
588 		fi->decimal_digits = pgtype_decimal_digits(stmt, type, i);
589 		fi->display_size = pgtype_display_size(stmt, type, i, unknowns_as);
590 	}
591 
592 	if (NAME_IS_NULL(fi->column_name))
593 	{
594 		switch (fi->attnum)
595 		{
596 			case CTID_ATTNUM:
597 				STR_TO_NAME(fi->column_name, "ctid");
598 				break;
599 			case OID_ATTNUM:
600 				STR_TO_NAME(fi->column_name, OID_NAME);
601 				break;
602 			case XMIN_ATTNUM:
603 				STR_TO_NAME(fi->column_name, XMIN_NAME);
604 				break;
605 		}
606 	}
607 }
608 
609 /*
610  *	SQLColAttribute tries to set the FIELD_INFO (using protocol 3).
611  */
612 static BOOL
ColAttSet(StatementClass * stmt,TABLE_INFO * rti)613 ColAttSet(StatementClass *stmt, TABLE_INFO *rti)
614 {
615 	CSTR		func = "ColAttSet";
616 	QResultClass	*res = SC_get_ExecdOrParsed(stmt);
617 	IRDFields	*irdflds = SC_get_IRDF(stmt);
618 	COL_INFO	*col_info = NULL;
619 	FIELD_INFO	**fi, *wfi;
620 	OID		reloid = 0;
621 	Int2		attid;
622 	int		i, num_fields;
623 	BOOL		fi_reuse, updatable, call_xxxxx;
624 
625 MYLOG(0, "entering\n");
626 
627 	if (reloid = rti->table_oid, 0 == reloid)
628 		return FALSE;
629 	if (0 != (rti->flags & TI_COLATTRIBUTE))
630 		return TRUE;
631 	col_info = rti->col_info;
632 	if (!QR_command_maybe_successful(res))
633 		return FALSE;
634 	if (num_fields = QR_NumPublicResultCols(res), num_fields <= 0)
635 		return FALSE;
636 	fi = irdflds->fi;
637 	if (num_fields > (int) irdflds->allocated)
638 	{
639 		if (!allocateFields(irdflds, num_fields))
640 			return FALSE;
641 		fi = irdflds->fi;
642 	}
643 	setNumFields(irdflds, num_fields);
644 	updatable = TI_is_updatable(rti);
645 MYLOG(0, "updatable=%d tab=%d fields=%d", updatable, stmt->ntab, num_fields);
646 	if (updatable)
647 	{
648 		if (1 > stmt->ntab)
649 			updatable = FALSE;
650 	}
651 MYPRINTF(0, "->%d\n", updatable);
652 	for (i = 0; i < num_fields; i++)
653 	{
654 		if (reloid == (OID) QR_get_relid(res, i))
655 		{
656 			if (wfi = fi[i], NULL == wfi)
657 			{
658 				wfi = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
659 				if (wfi == NULL)
660 				{
661 					SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Couldn't allocate memory for field info.", func);
662 					return FALSE;
663 				}
664 				fi_reuse = FALSE;
665 				fi[i] = wfi;
666 			}
667 			else if (FI_is_applicable(wfi))
668 				continue;
669 			else
670 				fi_reuse = TRUE;
671 			FI_Constructor(wfi, fi_reuse);
672 			attid = (Int2) QR_get_attid(res, i);
673 			wfi->attnum = attid;
674 			wfi->basetype = QR_get_field_type(res, i);
675 			wfi->typmod = QR_get_atttypmod(res, i);
676 			call_xxxxx = TRUE;
677 			if (searchColInfo(col_info, wfi))
678 			{
679 				STR_TO_NAME(wfi->column_alias, QR_get_fieldname(res, i));
680 				wfi->basetype = QR_get_field_type(res, i);
681 				wfi->updatable = updatable;
682 				call_xxxxx = FALSE;
683 			}
684 			else
685 			{
686 				if (attid > 0)
687 				{
688 					if (getColumnsInfo(NULL, rti, reloid, stmt) &&
689 					    searchColInfo(col_info, wfi))
690 					{
691 						STR_TO_NAME(wfi->column_alias, QR_get_fieldname(res, i));
692 						wfi->basetype = QR_get_field_type(res, i);
693 						wfi->updatable = updatable;
694 						call_xxxxx= FALSE;
695 					}
696 				}
697 			}
698 			if (call_xxxxx)
699 				xxxxx(stmt, wfi, res, i);
700 			wfi->ti = rti;
701 			wfi->flag |= FIELD_COL_ATTRIBUTE;
702 		}
703 	}
704 	if (stmt->updatable < 0)
705 	{
706 		if (stmt->ntab > 1)
707 			updatable = FALSE;
708 		SC_set_updatable(stmt, updatable);
709 	}
710 
711 	rti->flags |= TI_COLATTRIBUTE;
712 	return TRUE;
713 }
714 
715 static BOOL
getCOLIfromTable(ConnectionClass * conn,pgNAME * schema_name,pgNAME table_name,COL_INFO ** coli)716 getCOLIfromTable(ConnectionClass *conn, pgNAME *schema_name, pgNAME table_name,
717 COL_INFO **coli)
718 {
719 	int	colidx;
720 	BOOL	found = FALSE;
721 
722 	*coli = NULL;
723 	if (NAME_IS_NULL(table_name))
724 		return TRUE;
725 	if (NAME_IS_NULL(*schema_name))
726 	{
727 		const char *curschema = CC_get_current_schema(conn);
728 		/*
729 		 * Though current_schema() doesn't have
730 		 * much sense in PostgreSQL, we first
731 		 * check the current_schema() when no
732 		 * explicit schema name is specified.
733 		 */
734 		if (curschema)
735 		{
736 			for (colidx = 0; colidx < conn->ntables; colidx++)
737 			{
738 				if (!NAMEICMP(conn->col_info[colidx]->table_name, table_name) &&
739 					!stricmp(SAFE_NAME(conn->col_info[colidx]->schema_name), curschema))
740 				{
741 					MYLOG(0, "FOUND col_info table='%s' current schema='%s'\n", PRINT_NAME(table_name), curschema);
742 					found = TRUE;
743 					STR_TO_NAME(*schema_name, curschema);
744 					break;
745 				}
746 			}
747 		}
748 		if (!found)
749 		{
750 			QResultClass	*res;
751 			char		token[256], relcnv[128];
752 			BOOL		tblFound = FALSE;
753 
754 			/*
755 			 * We also have to check as follows.
756 			 */
757 			SPRINTF_FIXED(token,
758 					 "select nspname from pg_namespace n, pg_class c"
759 					 " where c.relnamespace=n.oid and c.oid='%s'::regclass",
760 					 identifierEscape((const SQLCHAR *) SAFE_NAME(table_name), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE));
761 			res = CC_send_query(conn, token, NULL, READ_ONLY_QUERY, NULL);
762 			if (QR_command_maybe_successful(res))
763 			{
764 				if (QR_get_num_total_tuples(res) == 1)
765 				{
766 					tblFound = TRUE;
767 					STR_TO_NAME(*schema_name, QR_get_value_backend_text(res, 0, 0));
768 				}
769 			}
770 			QR_Destructor(res);
771 			if (!tblFound)
772 				return FALSE;
773 		}
774 	}
775 	if (!found && NAME_IS_VALID(*schema_name))
776 	{
777 		for (colidx = 0; colidx < conn->ntables; colidx++)
778 		{
779 			if (!NAMEICMP(conn->col_info[colidx]->table_name, table_name) &&
780 			    !NAMEICMP(conn->col_info[colidx]->schema_name, *schema_name))
781 			{
782 				MYLOG(0, "FOUND col_info table='%s' schema='%s'\n", PRINT_NAME(table_name), PRINT_NAME(*schema_name));
783 				found = TRUE;
784 				break;
785 			}
786 		}
787 	}
788 	*coli = found ? conn->col_info[colidx] : NULL;
789 	return TRUE; /* success */
790 }
791 
792 static BOOL
getColumnsInfo(ConnectionClass * conn,TABLE_INFO * wti,OID greloid,StatementClass * stmt)793 getColumnsInfo(ConnectionClass *conn, TABLE_INFO *wti, OID greloid, StatementClass *stmt)
794 {
795 	BOOL		found = FALSE;
796 	RETCODE		result;
797 	HSTMT		hcol_stmt = NULL;
798 	StatementClass	*col_stmt;
799 	QResultClass	*res;
800 
801 	MYLOG(0, "entering Getting PG_Columns for table %u(%s)\n", greloid, PRINT_NAME(wti->table_name));
802 
803 	if (NULL == conn)
804 		conn = SC_get_conn(stmt);
805 	result = PGAPI_AllocStmt(conn, &hcol_stmt, 0);
806 	if (!SQL_SUCCEEDED(result))
807 	{
808 		if (stmt)
809 			SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for columns.", __FUNCTION__);
810 		goto cleanup;
811 	}
812 
813 	col_stmt = (StatementClass *) hcol_stmt;
814 
815 	if (greloid)
816 		result = PGAPI_Columns(hcol_stmt, NULL, 0,
817 				NULL, 0, NULL, 0, NULL, 0,
818 				PODBC_SEARCH_BY_IDS, greloid, 0);
819 	else
820 		result = PGAPI_Columns(hcol_stmt, NULL, 0,
821 							   (SQLCHAR *) SAFE_NAME(wti->schema_name), SQL_NTS,
822 							   (SQLCHAR *) SAFE_NAME(wti->table_name), SQL_NTS,
823 							   NULL, 0,
824 							   PODBC_NOT_SEARCH_PATTERN, 0, 0);
825 
826 	MYLOG(0, "        Past PG_Columns\n");
827 	res = SC_get_ExecdOrParsed(col_stmt);
828 	if (SQL_SUCCEEDED(result)
829 		&& res != NULL && QR_get_num_cached_tuples(res) > 0)
830 	{
831 		BOOL		coli_exist = FALSE;
832 		COL_INFO	*coli = NULL, *ccoli = NULL, *tcoli;
833 		int		k;
834 		time_t		acctime = 0;
835 
836 		MYLOG(0, "      Success\n");
837 		if (greloid != 0)
838 		{
839 			for (k = 0; k < conn->ntables; k++)
840 			{
841 				tcoli = conn->col_info[k];
842 				if (tcoli->table_oid == greloid)
843 				{
844 					coli = tcoli;
845 					coli_exist = TRUE;
846 					break;
847 				}
848 			}
849 		}
850 		if (!coli_exist)
851 		{
852 			for (k = 0; k < conn->ntables; k++)
853 			{
854 				tcoli = conn->col_info[k];
855 				if (0 < tcoli->refcnt)
856 					continue;
857 				if ((0 == tcoli->table_oid &&
858 				    NAME_IS_NULL(tcoli->table_name)) ||
859 				    strnicmp(SAFE_NAME(tcoli->schema_name), "pg_temp_", 8) == 0)
860 				{
861 					coli = tcoli;
862 					coli_exist = TRUE;
863 					break;
864 				}
865 				if (NULL == ccoli ||
866 				    tcoli->acc_time < acctime)
867 				{
868 					ccoli = tcoli;
869 					acctime = tcoli->acc_time;
870 				}
871 			}
872 			if (!coli_exist &&
873 			    NULL != ccoli &&
874 			    conn->ntables >= COLI_RECYCLE)
875 			{
876 				coli_exist = TRUE;
877 				coli = ccoli;
878 			}
879 		}
880 		if (coli_exist)
881 		{
882 			free_col_info_contents(coli);
883 		}
884 		else
885 		{
886 			if (conn->ntables >= conn->coli_allocated)
887 			{
888 				Int2	new_alloc;
889 				COL_INFO **col_info;
890 
891 				new_alloc = conn->coli_allocated * 2;
892 				if (new_alloc <= conn->ntables)
893 					new_alloc = COLI_INCR;
894 				MYLOG(0, "PARSE: Allocating col_info at ntables=%d\n", conn->ntables);
895 
896 				col_info = (COL_INFO **) realloc(conn->col_info, new_alloc * sizeof(COL_INFO *));
897 				if (!col_info)
898 				{
899 					if (stmt)
900 						SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for col_info.", __FUNCTION__);
901 					goto cleanup;
902 				}
903 				conn->col_info = col_info;
904 				conn->coli_allocated = new_alloc;
905 			}
906 
907 			MYLOG(0, "PARSE: malloc at conn->col_info[%d]\n", conn->ntables);
908 			coli = conn->col_info[conn->ntables] = (COL_INFO *) malloc(sizeof(COL_INFO));
909 		}
910 		if (!coli)
911 		{
912 			if (stmt)
913 				SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for col_info(2).", __FUNCTION__);
914 			goto cleanup;
915 		}
916 		col_info_initialize(coli);
917 
918 		coli->result = res;
919 		if (res && QR_get_num_cached_tuples(res) > 0)
920 		{
921 			int num_tuples = QR_get_num_cached_tuples(res);
922 			int i;
923 
924 			if (!greloid)
925 				greloid = (OID) strtoul(QR_get_value_backend_text(res, 0, COLUMNS_TABLE_OID), NULL, 10);
926 			if (!wti->table_oid)
927 				wti->table_oid = greloid;
928 			if (NAME_IS_NULL(wti->schema_name))
929 				STR_TO_NAME(wti->schema_name,
930 					QR_get_value_backend_text(res, 0, COLUMNS_SCHEMA_NAME));
931 			if (NAME_IS_NULL(wti->table_name))
932 				STR_TO_NAME(wti->table_name,
933 					QR_get_value_backend_text(res, 0, COLUMNS_TABLE_NAME));
934 			for (i = 0; i < num_tuples; i++)
935 			{
936 				if (NULL != QR_get_value_backend_text(res, 0, COLUMNS_TABLE_INFO))
937 				{
938 					coli->table_info = QR_get_value_backend_int(res, 0, COLUMNS_TABLE_INFO, NULL);
939 					break;
940 				}
941 			}
942 		}
943 MYLOG(DETAIL_LOG_LEVEL, "#2 %p->table_name=%s(%u)\n", wti, PRINT_NAME(wti->table_name), wti->table_oid);
944 		/*
945 		 * Store the table name and the SQLColumns result
946 		 * structure
947 		 */
948 		if (NAME_IS_VALID(wti->schema_name))
949 		{
950 			NAME_TO_NAME(coli->schema_name,  wti->schema_name);
951 		}
952 		else
953 			NULL_THE_NAME(coli->schema_name);
954 		NAME_TO_NAME(coli->table_name, wti->table_name);
955 		coli->table_oid = wti->table_oid;
956 
957 		/*
958 		 * The connection will now free the result structures, so
959 		 * make sure that the statement doesn't free it
960 		 */
961 		SC_init_Result(col_stmt);
962 
963 		if (!coli_exist)
964 			conn->ntables++;
965 
966 if (res && QR_get_num_cached_tuples(res) > 0)
967 MYLOG(DETAIL_LOG_LEVEL, "oid item == %s\n", (const char *) QR_get_value_backend_text(res, 0, 3));
968 
969 		MYLOG(0, "Created col_info table='%s', ntables=%d\n", PRINT_NAME(wti->table_name), conn->ntables);
970 		/* Associate a table from the statement with a SQLColumn info */
971 		found = TRUE;
972 		coli->refcnt++;
973 		wti->col_info = coli;
974 	}
975 cleanup:
976 	if (hcol_stmt)
977 		PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
978 	return found;
979 }
980 
getCOLIfromTI(const char * func,ConnectionClass * conn,StatementClass * stmt,const OID reloid,TABLE_INFO ** pti)981 BOOL getCOLIfromTI(const char *func, ConnectionClass *conn, StatementClass *stmt, const OID reloid, TABLE_INFO **pti)
982 {
983 	BOOL	colatt = FALSE, found = FALSE;
984 	OID	greloid = reloid;
985 	TABLE_INFO	*wti = *pti;
986 	COL_INFO	*coli;
987 
988 MYLOG(DETAIL_LOG_LEVEL, "entering reloid=%u ti=%p\n", reloid, wti);
989 	if (!conn)
990 		conn = SC_get_conn(stmt);
991 	if (!wti)	/* SQLColAttribute case */
992 	{
993 		int	i;
994 
995 		if (0 == greloid)
996 			return FALSE;
997 		if (!stmt)
998 			return FALSE;
999 		colatt = TRUE;
1000 		for (i = 0; i < stmt->ntab; i++)
1001 		{
1002 			if (stmt->ti[i]->table_oid == greloid)
1003 			{
1004 				wti = stmt->ti[i];
1005 				break;
1006 			}
1007 		}
1008 		if (!wti)
1009 		{
1010 MYLOG(DETAIL_LOG_LEVEL, "before increaseNtab\n");
1011 			if (!increaseNtab(stmt, func))
1012 				return FALSE;
1013 			wti = stmt->ti[stmt->ntab - 1];
1014 			wti->table_oid = greloid;
1015 		}
1016 		*pti = wti;
1017 	}
1018 MYLOG(DETAIL_LOG_LEVEL, "fi=%p greloid=%d col_info=%p\n", wti, greloid, wti->col_info);
1019 	if (0 == greloid)
1020 		greloid = wti->table_oid;
1021 	if (NULL != wti->col_info)
1022 	{
1023 		found = TRUE;
1024 		goto cleanup;
1025 	}
1026 	if (greloid != 0)
1027 	{
1028 		int	colidx;
1029 
1030 		for (colidx = 0; colidx < conn->ntables; colidx++)
1031 		{
1032 			if (conn->col_info[colidx]->table_oid == greloid)
1033 			{
1034 				MYLOG(0, "FOUND col_info table=%ul\n", greloid);
1035 				found = TRUE;
1036 				wti->col_info = conn->col_info[colidx];
1037 				wti->col_info->refcnt++;
1038 				break;
1039 			}
1040 		}
1041 	}
1042 	else
1043 	{
1044 		if (!getCOLIfromTable(conn, &wti->schema_name, wti->table_name, &coli))
1045 		{
1046 			if (stmt)
1047 			{
1048 				SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1049 				SC_set_error(stmt, STMT_EXEC_ERROR, "Table not found", func);
1050 				SC_reset_updatable(stmt);
1051 			}
1052 			return FALSE;
1053 		}
1054 		else if (NULL != coli)
1055 		{
1056 			found = TRUE;
1057 			coli->refcnt++;
1058 			wti->col_info = coli;
1059 		}
1060 	}
1061 	if (found)
1062 		goto cleanup;
1063 	else if (0 != greloid || NAME_IS_VALID(wti->table_name))
1064 		found = getColumnsInfo(conn, wti, greloid, stmt);
1065 cleanup:
1066 	if (found)
1067 	{
1068 		QResultClass	*res = wti->col_info->result;
1069 
1070 		if (res && QR_get_num_cached_tuples(res) > 0)
1071 		{
1072 			if (!greloid)
1073 				greloid = (OID) strtoul(QR_get_value_backend_text(res, 0, COLUMNS_TABLE_OID), NULL, 10);
1074 			if (!wti->table_oid)
1075 				wti->table_oid = greloid;
1076 			if (NAME_IS_NULL(wti->schema_name))
1077 				STR_TO_NAME(wti->schema_name,
1078 					QR_get_value_backend_text(res, 0, COLUMNS_SCHEMA_NAME));
1079 			if (NAME_IS_NULL(wti->table_name))
1080 				STR_TO_NAME(wti->table_name,
1081 					QR_get_value_backend_text(res, 0, COLUMNS_TABLE_NAME));
1082 		}
1083 MYLOG(DETAIL_LOG_LEVEL, "#1 %p->table_name=%s(%u)\n", wti, PRINT_NAME(wti->table_name), wti->table_oid);
1084 		if (colatt /* SQLColAttribute case */
1085 		    && 0 == (wti->flags & TI_COLATTRIBUTE))
1086 		{
1087 			if (stmt)
1088 				ColAttSet(stmt, wti);
1089 		}
1090 		wti->col_info->acc_time = SC_get_time(stmt);
1091 	}
1092 	else if (!colatt && stmt)
1093 		SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1094 MYLOG(DETAIL_LOG_LEVEL, "leaving returns %d\n", found);
1095 	return found;
1096 }
1097 
1098 SQLRETURN
SC_set_SS_columnkey(StatementClass * stmt)1099 SC_set_SS_columnkey(StatementClass *stmt)
1100 {
1101 	IRDFields	*irdflds = SC_get_IRDF(stmt);
1102 	FIELD_INFO	**fi = irdflds->fi, *tfi;
1103 	size_t		nfields = irdflds->nfields;
1104 	HSTMT		pstmt = NULL;
1105 	SQLRETURN	ret = SQL_SUCCESS;
1106 	BOOL		contains_key = FALSE;
1107 	int		i;
1108 
1109 MYLOG(DETAIL_LOG_LEVEL, "entering fields=" FORMAT_SIZE_T " ntab=%d\n", nfields, stmt->ntab);
1110 	if (!fi)		return ret;
1111 	if (0 >= nfields)	return ret;
1112 	for (i = 0; i < stmt->ntab; i++)
1113 	{
1114 		TABLE_INFO	**ti = stmt->ti, *oneti;
1115 		ConnectionClass *conn = SC_get_conn(stmt);
1116 		OID	internal_asis_type = SQL_C_CHAR;
1117 		char		keycolnam[MAX_INFO_STRING];
1118 		SQLLEN		keycollen;
1119 
1120 		ret = PGAPI_AllocStmt(conn, &pstmt, 0);
1121 		if (!SQL_SUCCEEDED(ret))
1122 			return ret;
1123 		oneti = ti[i];
1124 		ret = PGAPI_PrimaryKeys(pstmt, NULL, 0, NULL, 0, NULL, 0, oneti->table_oid);
1125 		if (!SQL_SUCCEEDED(ret))
1126 			goto cleanup;
1127 #ifdef	UNICODE_SUPPORT
1128 		if (CC_is_in_unicode_driver(conn))
1129 			internal_asis_type = INTERNAL_ASIS_TYPE;
1130 #endif /* UNICODE_SUPPORT */
1131 		ret = PGAPI_BindCol(pstmt, 4, internal_asis_type, keycolnam, MAX_INFO_STRING, &keycollen);
1132 		if (!SQL_SUCCEEDED(ret))
1133 			goto cleanup;
1134 		contains_key = TRUE;
1135 		ret = PGAPI_Fetch(pstmt);
1136 		while (SQL_SUCCEEDED(ret))
1137 		{
1138 			int	i;	// different from i of outer loop
1139 
1140 			for (i = 0; i < nfields; i++)
1141 			{
1142 				if (tfi = fi[i], NULL == tfi)
1143 					continue;
1144 				if (!FI_is_applicable(tfi))
1145 					continue;
1146 				if (oneti == tfi->ti &&
1147 				    strcmp(keycolnam, SAFE_NAME(tfi->column_name)) == 0)
1148 				{
1149 MYLOG(DETAIL_LOG_LEVEL, "key %s found at %p\n", keycolnam, fi + i);
1150 					tfi->columnkey = TRUE;
1151 					break;
1152 				}
1153 			}
1154 			if (i >= nfields)
1155 			{
1156 				MYLOG(0, "%s not found\n", keycolnam);
1157 				break;
1158 			}
1159 			ret = PGAPI_Fetch(pstmt);
1160 		}
1161 		if (SQL_SUCCEEDED(ret))
1162 			contains_key = FALSE;
1163 		else if (SQL_NO_DATA_FOUND != ret)
1164 			goto cleanup;
1165 		ret = SQL_SUCCESS;
1166 	}
1167 MYLOG(DETAIL_LOG_LEVEL, "contains_key=%d\n", contains_key);
1168 	for (i = 0; i < nfields; i++)
1169 	{
1170 		if (tfi = fi[i], NULL == tfi)
1171 			continue;
1172 		if (!FI_is_applicable(tfi))
1173 			continue;
1174 		if (!contains_key || tfi->columnkey < 0)
1175 			tfi->columnkey = FALSE;
1176 	}
1177 cleanup:
1178 	if (pstmt)
1179 		PGAPI_FreeStmt(pstmt, SQL_DROP);
1180 	return ret;
1181 }
1182 
include_alias_wo_as(const char * token,const char * btoken)1183 static BOOL include_alias_wo_as(const char *token, const char *btoken)
1184 {
1185 MYLOG(0, "alias ? token=%s btoken=%s\n", token, btoken);
1186 	if ('\0' == btoken[0])
1187 		return FALSE;
1188 	else if (0 == stricmp(")", token))
1189 		return FALSE;
1190 	else if (0 == stricmp("as", btoken) ||
1191 		 0 == stricmp("and", btoken) ||
1192 		 0 == stricmp("or", btoken) ||
1193 		 0 == stricmp("not", btoken) ||
1194 		 0 == stricmp(",", btoken))
1195 		return FALSE;
1196 	else
1197 	{
1198 		CSTR ops = "+-*/%^|!@&#~<>=.";
1199 		const char *cptr, *optr;
1200 
1201 		for (cptr = btoken; *cptr; cptr++)
1202 		{
1203 			for (optr = ops; *optr; optr++)
1204 			{
1205 				if (*optr != *cptr)
1206 					return TRUE;
1207 			}
1208 		}
1209 	}
1210 
1211 	return FALSE;
1212 }
1213 
insert_as_to_the_statement(char * stmt,const char ** pptr,const char ** ptr)1214 static char *insert_as_to_the_statement(char *stmt, const char **pptr, const char **ptr)
1215 {
1216 	size_t	stsize = strlen(stmt), ppos = *pptr - stmt, remsize = stsize - ppos;
1217 	const int  ins_size = 3;
1218 	char	*newstmt = realloc(stmt, stsize + ins_size + 1);
1219 
1220 	if (newstmt)
1221 	{
1222 		char	*sptr = newstmt + ppos;
1223 		memmove(sptr + ins_size, sptr, remsize + 1);
1224 		sptr[0] = 'a';
1225 		sptr[1] = 's';
1226 		sptr[2] = ' ';
1227 		*ptr = sptr + (*ptr - *pptr) + ins_size;
1228 		*pptr = sptr + ins_size;
1229 	}
1230 
1231 	return newstmt;
1232 }
1233 
1234 #define	TOKEN_SIZE	256
1235 static char
parse_the_statement(StatementClass * stmt,BOOL check_hasoids,BOOL sqlsvr_check)1236 parse_the_statement(StatementClass *stmt, BOOL check_hasoids, BOOL sqlsvr_check)
1237 {
1238 	CSTR		func = "parse_the_statement";
1239 	char		delimdsp[2], token[TOKEN_SIZE], stoken[TOKEN_SIZE], btoken[TOKEN_SIZE];
1240 	char		delim,
1241 				quote,
1242 				dquote,
1243 				numeric,
1244 				unquoted;
1245 	const char *ptr;
1246 	const char *pptr = NULL;
1247 	char		in_select = FALSE,
1248 				in_distinct = FALSE,
1249 				in_on = FALSE,
1250 				in_from = FALSE,
1251 				in_where = FALSE,
1252 				in_table = FALSE,
1253 				out_table = TRUE;
1254 	char		in_field = FALSE,
1255 				in_expr = FALSE,
1256 				in_func = FALSE,
1257 				in_dot = FALSE,
1258 				in_as = FALSE;
1259 	int			j,
1260 				i,
1261 				k = 0,
1262 				n,
1263 				blevel = 0, old_blevel, subqlevel = 0,
1264 				tbl_blevel = 0, allocated_size = -1, new_size,
1265 				nfields;
1266 	FIELD_INFO **fi, *wfi;
1267 	TABLE_INFO **ti, *wti;
1268 	char		parse = FALSE;
1269 	po_ind_t	join_info = STMT_HAS_NO_JOIN;
1270 	ConnectionClass *conn = SC_get_conn(stmt);
1271 	IRDFields	*irdflds;
1272 	BOOL		updatable = TRUE, column_has_alias = FALSE, fupdatable;
1273 
1274 	MYLOG(0, "entering...\n");
1275 
1276 	if (SC_parsed_status(stmt) != STMT_PARSE_NONE)
1277 	{
1278 		if (check_hasoids)
1279 			CheckPgClassInfo(stmt);
1280 		return TRUE;
1281 	}
1282 	nfields = 0;
1283 	wfi = NULL;
1284 	wti = NULL;
1285 	ptr = stmt->statement;
1286 	if (sqlsvr_check)
1287 	{
1288 		irdflds = NULL;
1289 		fi = NULL;
1290 		ti = NULL;
1291 	}
1292 	else
1293 	{
1294 		SC_set_updatable(stmt, FALSE);
1295 		irdflds = SC_get_IRDF(stmt);
1296 		fi = irdflds->fi;
1297 		ti = stmt->ti;
1298 
1299 		allocated_size = irdflds->allocated;
1300 		SC_initialize_cols_info(stmt, FALSE, TRUE);
1301 		stmt->from_pos = -1;
1302 		stmt->where_pos = -1;
1303 	}
1304 #define	return	DONT_CALL_RETURN_FROM_HERE???
1305 
1306 	delim = '\0';
1307 	token[0] = '\0';
1308 	while (pptr = (const char *) ptr, (delim != ',') ? STRCPY_FIXED(btoken, token) : (btoken[0] = '\0', 0), (ptr = getNextToken(conn->ccsc, CC_get_escape(conn), pptr, token, sizeof(token), &delim, &quote, &dquote, &numeric)) != NULL)
1309 	{
1310 		unquoted = !(quote || dquote);
1311 
1312 		if (delim)
1313 		{
1314 			delimdsp[0] = delim;
1315 			delimdsp[1] = '\0';
1316 		}
1317 		else
1318 			delimdsp[0] = '\0';
1319 		MYLOG(0, "unquoted=%d, quote=%d, dquote=%d, numeric=%d, delim='%s', token='%s', ptr='%s'\n", unquoted, quote, dquote, numeric, delimdsp, token, ptr);
1320 
1321 		old_blevel = blevel;
1322 		if (unquoted && blevel == 0)
1323 		{
1324 			if (in_select)
1325 			{
1326 				if (!stricmp(token, "distinct"))
1327 				{
1328 					in_distinct = TRUE;
1329 					updatable = FALSE;
1330 
1331 					MYLOG(0, "DISTINCT\n");
1332 					continue;
1333 				}
1334 				else if (!stricmp(token, "into"))
1335 				{
1336 					in_select = FALSE;
1337 					MYLOG(0, "INTO\n");
1338 					stmt->statement_type = STMT_TYPE_CREATE;
1339 					SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1340 					goto cleanup;
1341 				}
1342 				else if (!stricmp(token, "from"))
1343 				{
1344 					if (sqlsvr_check)
1345 					{
1346 						parse = TRUE;
1347 						goto cleanup;
1348 					}
1349 					in_select = FALSE;
1350 					in_from = TRUE;
1351 					if (stmt->from_pos < 0 &&
1352 						(!strnicmp(pptr, "from", 4)))
1353 					{
1354 						MYLOG(0, "First From\n");
1355 						stmt->from_pos = pptr - stmt->statement;
1356 					}
1357 					else
1358 						MYLOG(0, "FROM\n");
1359 					continue;
1360 				}
1361 			} /* in_select && unquoted && blevel == 0 */
1362 			else if ((!stricmp(token, "where") ||
1363 				 !stricmp(token, "union") ||
1364 				 !stricmp(token, "intersect") ||
1365 				 !stricmp(token, "except") ||
1366 				 !stricmp(token, "order") ||
1367 				 !stricmp(token, "group") ||
1368 				 !stricmp(token, "having")))
1369 			{
1370 				in_from = FALSE;
1371 				in_where = TRUE;
1372 
1373 				if (stmt->where_pos < 0)
1374 					stmt->where_pos = pptr - stmt->statement;
1375 				MYLOG(0, "%s...\n", token);
1376 				if (stricmp(token, "where") &&
1377 				    stricmp(token, "order"))
1378 				{
1379 					updatable = FALSE;
1380 					break;
1381 				}
1382 				continue;
1383 			}
1384 		} /* unquoted && blevel == 0 */
1385 		/* check the change of blevel etc */
1386 		if (unquoted)
1387 		{
1388 			if (!stricmp(token, "select"))
1389 			{
1390 				stoken[0] = '\0';
1391 				if (0 == blevel)
1392 				{
1393 					in_select = TRUE;
1394 					MYLOG(0, "SELECT\n");
1395 					continue;
1396 				}
1397 				else
1398 				{
1399 					MYLOG(0, "SUBSELECT\n");
1400 					if (0 == subqlevel)
1401 						subqlevel = blevel;
1402 				}
1403 			}
1404 			else if (token[0] == '(')
1405 			{
1406 				blevel++;
1407 				MYLOG(0, "blevel++ -> %d\n", blevel);
1408 				/* aggregate function ? */
1409 				if (stoken[0] && updatable && 0 == subqlevel)
1410 				{
1411 					if (stricmp(stoken, "count") == 0 ||
1412 					    stricmp(stoken, "sum") == 0 ||
1413 					    stricmp(stoken, "avg") == 0 ||
1414 					    stricmp(stoken, "max") == 0 ||
1415 					    stricmp(stoken, "min") == 0 ||
1416 					    stricmp(stoken, "variance") == 0 ||
1417 					    stricmp(stoken, "stddev") == 0)
1418 						updatable = FALSE;
1419 				}
1420 			}
1421 			else if (token[0] == ')')
1422 			{
1423 				blevel--;
1424 				MYLOG(0, "blevel-- = %d\n", blevel);
1425 				if (blevel < subqlevel)
1426 					subqlevel = 0;
1427 			}
1428 			if (blevel >= old_blevel && ',' != delim)
1429 				STRCPY_FIXED(stoken, token);
1430 			else
1431 				stoken[0] = '\0';
1432 		}
1433 		if (in_select)
1434 		{
1435 MYLOG(0, "blevel=%d btoken=%s in_dot=%d in_field=%d tbname=%s\n", blevel, btoken, in_dot, in_field, wfi ? SAFE_NAME(wfi->column_alias) : "<null>");
1436 			if (0 == blevel &&
1437 			    sqlsvr_check &&
1438 			    dquote &&
1439 			    '\0' != btoken[0] &&
1440 			    !in_dot &&
1441 			    in_field &&
1442 			    (!column_has_alias))
1443 			{
1444 				if (include_alias_wo_as(token, btoken))
1445 				{
1446 					char *news;
1447 
1448 					column_has_alias = TRUE;
1449 					if (NULL != wfi)
1450 						STRX_TO_NAME(wfi->column_alias, token);
1451 					news = insert_as_to_the_statement(stmt->statement, &pptr, &ptr);
1452 					if (news != stmt->statement)
1453 					{
1454 						free(stmt->statement);
1455 						stmt->statement = news;
1456 					}
1457 				}
1458 			}
1459 			if (in_expr || in_func)
1460 			{
1461 				/* just eat the expression */
1462 				MYLOG(0, "in_expr=%d or func=%d\n", in_expr, in_func);
1463 
1464 				if (blevel == 0)
1465 				{
1466 					if (delim == ',')
1467 					{
1468 						MYLOG(0, "**** Got comma in_expr/func\n");
1469 						in_func = FALSE;
1470 						in_expr = FALSE;
1471 						in_field = FALSE;
1472 					}
1473 					else if (unquoted && !stricmp(token, "as"))
1474 					{
1475 						MYLOG(0, "got AS in_expr\n");
1476 						in_func = FALSE;
1477 						in_expr = FALSE;
1478 						in_as = TRUE;
1479 						in_field = TRUE;
1480 					}
1481 				}
1482 				continue;
1483 			} /* (in_expr || in_func) && in_select */
1484 
1485 			if (in_distinct)
1486 			{
1487 				MYLOG(0, "in distinct\n");
1488 
1489 				if (unquoted && !stricmp(token, "on"))
1490 				{
1491 					in_on = TRUE;
1492 					MYLOG(0, "got on\n");
1493 					continue;
1494 				}
1495 				if (in_on)
1496 				{
1497 					in_distinct = FALSE;
1498 					in_on = FALSE;
1499 					continue;	/* just skip the unique on field */
1500 				}
1501 				MYLOG(0, "done distinct\n");
1502 				in_distinct = FALSE;
1503 			} /* in_distinct */
1504 
1505 			if (!in_field)
1506 			{
1507 				BOOL	fi_reuse = FALSE;
1508 
1509 				if (!token[0])
1510 					continue;
1511 
1512 				column_has_alias = FALSE;
1513 				if (!sqlsvr_check)
1514 				{
1515 					/* if (!(irdflds->nfields % FLD_INCR)) */
1516 					if (irdflds->nfields >= allocated_size)
1517 					{
1518 						MYLOG(0, "reallocing at nfld=%d\n", irdflds->nfields);
1519 						new_size = irdflds->nfields + 1;
1520 						if (!allocateFields(irdflds, new_size))
1521 						{
1522 							SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1523 							SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for FIELD_INFO.", func);
1524 							goto cleanup;
1525 						}
1526 						fi = irdflds->fi;
1527 						allocated_size = irdflds->allocated;
1528 					}
1529 
1530 					wfi = fi[irdflds->nfields];
1531 					if (NULL != wfi)
1532 						fi_reuse = TRUE;
1533 					else
1534 						wfi = fi[irdflds->nfields] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
1535 					if (NULL == wfi)
1536 					{
1537 						SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1538 						SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for FIELD_INFO(2).", func);
1539 						goto cleanup;
1540 					}
1541 
1542 					/* Initialize the field info */
1543 					FI_Constructor(wfi, fi_reuse);
1544 					wfi->flag = FIELD_PARSING;
1545 				}
1546 
1547 				/* double quotes are for qualifiers */
1548 				if (dquote && NULL != wfi)
1549 					wfi->dquote = TRUE;
1550 
1551 				if (quote)
1552 				{
1553 					if (NULL != wfi)
1554 					{
1555 						wfi->quote = TRUE;
1556 						wfi->column_size = (int) strlen(token);
1557 					}
1558 				}
1559 				else if (numeric)
1560 				{
1561 					MYLOG(0, "**** got numeric: nfld = %d\n", nfields);
1562 					if (NULL != wfi)
1563 						wfi->numeric = TRUE;
1564 				}
1565 				else if (0 == old_blevel && blevel > 0)
1566 				{				/* expression */
1567 					MYLOG(0, "got EXPRESSION\n");
1568 					if (NULL != wfi)
1569 						wfi->expr = TRUE;
1570 					in_expr = TRUE;
1571 					/* continue; */
1572 				}
1573 				else if (NULL != wfi)
1574 				{
1575 					STRX_TO_NAME(wfi->column_name, token);
1576 					NULL_THE_NAME(wfi->before_dot);
1577 				}
1578 				if (NULL != wfi)
1579 					MYLOG(0, "got field='%s', dot='%s'\n", PRINT_NAME(wfi->column_name), PRINT_NAME(wfi->before_dot));
1580 
1581 				if (delim == ',')
1582 					MYLOG(0, "comma (1)\n");
1583 				else
1584 					in_field = TRUE;
1585 				nfields++;
1586 				if (NULL != irdflds)
1587 					irdflds->nfields++;
1588 				continue;
1589 			} /* !in_field */
1590 
1591 			/*
1592 			 * We are in a field now
1593 			 */
1594 			if (!sqlsvr_check)
1595 				wfi = fi[irdflds->nfields - 1];
1596 			if (in_dot)
1597 			{
1598 				if (NULL != wfi)
1599 				{
1600 					if (NAME_IS_VALID(wfi->before_dot))
1601 					{
1602 						MOVE_NAME(wfi->schema_name, wfi->before_dot);
1603 					}
1604 					MOVE_NAME(wfi->before_dot, wfi->column_name);
1605 					STRX_TO_NAME(wfi->column_name, token);
1606 				}
1607 
1608 				if (delim == ',')
1609 				{
1610 					MYLOG(0, "in_dot: got comma\n");
1611 					in_field = FALSE;
1612 				}
1613 				in_dot = FALSE;
1614 				continue;
1615 			}
1616 
1617 			if (in_as)
1618 			{
1619 				column_has_alias = TRUE;
1620 				if (NULL != wfi)
1621 				{
1622 					STRX_TO_NAME(wfi->column_alias, token);
1623 					MYLOG(0, "alias for field '%s' is '%s'\n", PRINT_NAME(wfi->column_name), PRINT_NAME(wfi->column_alias));
1624 				}
1625 				in_as = FALSE;
1626 				in_field = FALSE;
1627 
1628 				if (delim == ',')
1629 					MYLOG(0, "comma(2)\n");
1630 				continue;
1631 			}
1632 
1633 			/* Function */
1634 			if (0 == old_blevel && blevel > 0)
1635 			{
1636 				in_dot = FALSE;
1637 				in_func = TRUE;
1638 				if (NULL != wfi)
1639 				{
1640 					wfi->func = TRUE;
1641 
1642 					/*
1643 					 * name will have the function name -- maybe useful some
1644 					 * day
1645 					 */
1646 					MYLOG(0, "**** got function = '%s'\n", PRINT_NAME(wfi->column_name));
1647 				}
1648 				continue;
1649 			}
1650 
1651 			if (token[0] == '.')
1652 			{
1653 				in_dot = TRUE;
1654 				MYLOG(0, "got dot\n");
1655 				continue;
1656 			}
1657 
1658 			in_dot = FALSE;
1659 			if (!stricmp(token, "as"))
1660 			{
1661 				in_as = TRUE;
1662 				MYLOG(0, "got AS\n");
1663 				continue;
1664 			}
1665 
1666 			/* otherwise, it's probably an expression */
1667 			if (!column_has_alias)
1668 			{
1669 				in_expr = TRUE;
1670 				if (NULL != wfi)
1671 				{
1672 					wfi->expr = TRUE;
1673 					NULL_THE_NAME(wfi->column_name);
1674 					wfi->column_size = 0;
1675 				}
1676 				MYLOG(0, "*** setting expression\n");
1677 			}
1678 			else
1679 				MYLOG(0, "*** may be an alias for a field\n");
1680 			if (0 == blevel && ',' == delim)
1681 			{
1682 				in_expr = in_func = in_field = FALSE;
1683 			}
1684 		} /* in_select end */
1685 
1686 		if (in_from || in_where)
1687 		{
1688 			if (token[0] == ';') /* end of the first command */
1689 			{
1690 				in_select = in_from = in_where = in_table = FALSE;
1691 				break;
1692 			}
1693 		}
1694 		if (in_from)
1695 		{
1696 			switch (token[0])
1697 			{
1698 				case '\0':
1699 					continue;
1700 				case ',':
1701 					out_table = TRUE;
1702 					continue;
1703 			}
1704 			if (out_table && !in_table) /* new table */
1705 			{
1706 				BOOL	is_table_name, is_subquery;
1707 
1708 				in_dot = FALSE;
1709 				join_info = STMT_HAS_NO_JOIN;
1710 				if (!dquote)
1711 				{
1712 					if (token[0] == '(' ||
1713 					    token[0] == ')')
1714 						continue;
1715 				}
1716 
1717 				if (sqlsvr_check)
1718 					wti = NULL;
1719 				else
1720 				{
1721 					if (!increaseNtab(stmt, func))
1722 					{
1723 						SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1724 						goto cleanup;
1725 					}
1726 
1727 					ti = stmt->ti;
1728 					wti = ti[stmt->ntab - 1];
1729 				}
1730 				is_table_name = TRUE;
1731 				is_subquery = FALSE;
1732 				if (dquote)
1733 					;
1734 				else if (0 == stricmp(token, "select"))
1735 				{
1736 					MYLOG(0, "got subquery lvl=%d\n", blevel);
1737 					is_table_name = FALSE;
1738 					is_subquery = TRUE;
1739 				}
1740 				else if ('(' == ptr[0])
1741 				{
1742 					MYLOG(0, "got srf? = '%s'\n", token);
1743 					is_table_name = FALSE;
1744 				}
1745 				if (NULL != wti)
1746 				{
1747 					if (is_table_name)
1748 					{
1749 						STRX_TO_NAME(wti->table_name, token);
1750 						lower_the_name(GET_NAME(wti->table_name), conn, dquote);
1751 						MYLOG(0, "got table = '%s'\n", PRINT_NAME(wti->table_name));
1752 					}
1753 					else
1754 					{
1755 						NULL_THE_NAME(wti->table_name);
1756 						TI_no_updatable(wti);
1757 					}
1758 				}
1759 
1760 				if (0 == blevel && delim == ',')
1761 				{
1762 					out_table = TRUE;
1763 					MYLOG(0, "more than 1 tables\n");
1764 				}
1765 				else
1766 				{
1767 					out_table = FALSE;
1768 					in_table = TRUE;
1769 					if (is_subquery)
1770 						tbl_blevel = blevel - 1;
1771 					else
1772 						tbl_blevel = blevel;
1773 				}
1774 				continue;
1775 			}
1776 			if (blevel > tbl_blevel)
1777 				continue;
1778 			/* out_table is FALSE here */
1779 			if (!dquote && !in_dot)
1780 			{
1781 				if (')' == token[0])
1782 					continue;
1783 				if (stricmp(token, "LEFT") == 0 ||
1784 					stricmp(token, "RIGHT") == 0 ||
1785 					stricmp(token, "OUTER") == 0 ||
1786 					stricmp(token, "FULL") == 0)
1787 				{
1788 					join_info = STMT_HAS_OUTER_JOIN;
1789 					in_table = FALSE;
1790 					continue;
1791 				}
1792 				else if (stricmp(token, "INNER") == 0 ||
1793 						 stricmp(token, "CROSS") == 0)
1794 				{
1795 					join_info = STMT_HAS_INNER_JOIN;
1796 					in_table = FALSE;
1797 					continue;
1798 				}
1799 				else if (stricmp(token, "JOIN") == 0)
1800 				{
1801 					in_table = FALSE;
1802 					out_table = TRUE;
1803 					if (join_info == STMT_HAS_OUTER_JOIN)
1804 					{
1805 						SC_set_outer_join(stmt);
1806 					}
1807 					else
1808 					{
1809 						SC_set_inner_join(stmt);
1810 					}
1811 					join_info = STMT_HAS_NO_JOIN;
1812 					continue;
1813 				}
1814 			}
1815 			join_info = STMT_HAS_NO_JOIN;
1816 			if (in_table)
1817 			{
1818 				if (!sqlsvr_check)
1819 					wti = ti[stmt->ntab - 1];
1820 				if (in_dot)
1821 				{
1822 					if (NULL != wfi)
1823 					{
1824 						MOVE_NAME(wti->schema_name, wti->table_name);
1825 						STRX_TO_NAME(wti->table_name, token);
1826 						lower_the_name(GET_NAME(wti->table_name), conn, dquote);
1827 					}
1828 					in_dot = FALSE;
1829 					continue;
1830 				}
1831 				if (strcmp(token, ".") == 0)
1832 				{
1833 					in_dot = TRUE;
1834 					continue;
1835 				}
1836 				if (dquote || stricmp(token, "as"))
1837 				{
1838 					if (!dquote)
1839 					{
1840 						if (stricmp(token, "ON") == 0)
1841 						{
1842 							in_table = FALSE;
1843 							continue;
1844 						}
1845 					}
1846 					if (NULL != wti)
1847 					{
1848 						STRX_TO_NAME(wti->table_alias, token);
1849 						MYLOG(0, "alias for table '%s' is '%s'\n", PRINT_NAME(wti->table_name), PRINT_NAME(wti->table_alias));
1850 					}
1851 					in_table = FALSE;
1852 					if (delim == ',')
1853 					{
1854 						out_table = TRUE;
1855 						MYLOG(0, "more than 1 tables\n");
1856 					}
1857 				}
1858 			}
1859 		} /* in_from */
1860 	}
1861 
1862 	/*
1863 	 * Resolve any possible field names with tables
1864 	 */
1865 
1866 	parse = TRUE;
1867 	if (sqlsvr_check)
1868 		goto cleanup;
1869 
1870 	/* Resolve field names with tables */
1871 	for (i = 0; i < (int) irdflds->nfields; i++)
1872 	{
1873 		wfi = fi[i];
1874 		if (wfi->func || wfi->expr || wfi->numeric)
1875 		{
1876 			wfi->ti = NULL;
1877 			wfi->columntype = wfi->basetype = (OID) 0;
1878 			parse = FALSE;
1879 			continue;
1880 		}
1881 		else if (wfi->quote)
1882 		{						/* handle as text */
1883 			wfi->ti = NULL;
1884 
1885 			/*
1886 			 * wfi->type = PG_TYPE_TEXT; wfi->column_size = 0; the
1887 			 * following may be better
1888 			 */
1889 			wfi->basetype = PG_TYPE_UNKNOWN;
1890 			if (wfi->column_size == 0)
1891 			{
1892 				wfi->basetype = PG_TYPE_VARCHAR;
1893 				wfi->column_size = 254;
1894 			}
1895 			wfi->length = wfi->column_size;
1896 			continue;
1897 		}
1898 		/* field name contains the schema name */
1899 		else if (NAME_IS_VALID(wfi->schema_name))
1900 		{
1901 			int	matchidx = -1;
1902 
1903 			for (k = 0; k < stmt->ntab; k++)
1904 			{
1905 				wti = ti[k];
1906 				if (!NAMEICMP(wti->table_name, wfi->before_dot))
1907 				{
1908 					if (!NAMEICMP(wti->schema_name, wfi->schema_name))
1909 					{
1910 						wfi->ti = wti;
1911 						break;
1912 					}
1913 					else if (NAME_IS_NULL(wti->schema_name))
1914 					{
1915 						if (matchidx < 0)
1916 							matchidx = k;
1917 						else
1918 						{
1919 							SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1920 							SC_set_error(stmt, STMT_EXEC_ERROR, "duplicated Table name", func);
1921 							SC_reset_updatable(stmt);
1922 							goto cleanup;
1923 						}
1924 					}
1925 				}
1926 			}
1927 			if (matchidx >= 0)
1928 				wfi->ti = ti[matchidx];
1929 		}
1930 		/* it's a dot, resolve to table or alias */
1931 		else if (NAME_IS_VALID(wfi->before_dot))
1932 		{
1933 			for (k = 0; k < stmt->ntab; k++)
1934 			{
1935 				wti = ti[k];
1936 				if (!NAMEICMP(wti->table_alias, wfi->before_dot))
1937 				{
1938 					wfi->ti = wti;
1939 					break;
1940 				}
1941 				else if (!NAMEICMP(wti->table_name, wfi->before_dot))
1942 				{
1943 					wfi->ti = wti;
1944 					break;
1945 				}
1946 			}
1947 		}
1948 		else if (stmt->ntab == 1)
1949 			wfi->ti = ti[0];
1950 	}
1951 
1952 	MYLOG(0, "--------------------------------------------\n");
1953 	MYLOG(0, "nfld=%d, ntab=%d\n", irdflds->nfields, stmt->ntab);
1954 	if (0 == stmt->ntab)
1955 	{
1956 		SC_set_parse_status(stmt, STMT_PARSE_FATAL);
1957 		goto cleanup;
1958 	}
1959 
1960 	for (i = 0; i < (int) irdflds->nfields; i++)
1961 	{
1962 		wfi = fi[i];
1963 		MYLOG(0, "Field %d:  expr=%d, func=%d, quote=%d, dquote=%d, numeric=%d, name='%s', alias='%s', dot='%s'\n", i, wfi->expr, wfi->func, wfi->quote, wfi->dquote, wfi->numeric, PRINT_NAME(wfi->column_name), PRINT_NAME(wfi->column_alias), PRINT_NAME(wfi->before_dot));
1964 		if (wfi->ti)
1965 			MYLOG(0, "     ----> table_name='%s', table_alias='%s'\n", PRINT_NAME(wfi->ti->table_name), PRINT_NAME(wfi->ti->table_alias));
1966 	}
1967 
1968 	for (i = 0; i < stmt->ntab; i++)
1969 	{
1970 		wti = ti[i];
1971 		MYLOG(0, "Table %d: name='%s', alias='%s'\n", i, PRINT_NAME(wti->table_name), PRINT_NAME(wti->table_alias));
1972 	}
1973 
1974 	/*
1975 	 * Now save the SQLColumns Info for the parse tables
1976 	 */
1977 
1978 	/* Call SQLColumns for each table and store the result */
1979 	if (stmt->ntab > 1)
1980 		updatable = FALSE;
1981 	else if (stmt->from_pos < 0)
1982 		updatable = FALSE;
1983 	for (i = 0; i < stmt->ntab; i++)
1984 	{
1985 		/* See if already got it */
1986 		wti = ti[i];
1987 
1988 		if (!getCOLIfromTI(func, NULL, stmt, 0, &wti))
1989 			break;
1990 	}
1991 	if (STMT_PARSE_FATAL == SC_parsed_status(stmt))
1992 	{
1993 		goto cleanup;
1994 	}
1995 
1996 	MYLOG(0, "Done PG_Columns\n");
1997 
1998 	/*
1999 	 * Now resolve the fields to point to column info
2000 	 */
2001 	for (i = 0; i < (int) irdflds->nfields;)
2002 	{
2003 		wfi = fi[i];
2004 		if (wfi->ti)
2005 			fupdatable = updatable && TI_is_updatable(wfi->ti);
2006 		else
2007 			fupdatable = FALSE;
2008 		wfi->updatable = fupdatable;
2009 		/* Dont worry about functions or quotes */
2010 		if (wfi->func || wfi->quote || wfi->numeric)
2011 		{
2012 			wfi->updatable = FALSE;
2013 			i++;
2014 			continue;
2015 		}
2016 
2017 		/* Stars get expanded to all fields in the table */
2018 		else if (SAFE_NAME(wfi->column_name)[0] == '*')
2019 		{
2020 			char		do_all_tables;
2021 			Int2			total_cols,
2022 						cols,
2023 						increased_cols;
2024 
2025 			MYLOG(0, "expanding field %d\n", i);
2026 
2027 			total_cols = 0;
2028 
2029 			if (wfi->ti)		/* The star represents only the qualified
2030 								 * table */
2031 				total_cols = (Int2) QR_get_num_cached_tuples(wfi->ti->col_info->result);
2032 
2033 			else
2034 			{	/* The star represents all tables */
2035 				/* Calculate the total number of columns after expansion */
2036 				for (k = 0; k < stmt->ntab; k++)
2037 					total_cols += (Int2) QR_get_num_cached_tuples(ti[k]->col_info->result);
2038 			}
2039 			increased_cols = total_cols - 1;
2040 
2041 			/* Allocate some more field pointers if necessary */
2042 			new_size = irdflds->nfields + increased_cols;
2043 
2044 			MYLOG(0, "k=%d, increased_cols=%d, allocated_size=%d, new_size=%d\n", k, increased_cols, allocated_size, new_size);
2045 
2046 			if (new_size > allocated_size)
2047 			{
2048 				int	new_alloc = new_size;
2049 
2050 				MYLOG(0, "need more cols: new_alloc = %d\n", new_alloc);
2051 				if (!allocateFields(irdflds, new_alloc))
2052 				{
2053 					SC_set_parse_status(stmt, STMT_PARSE_FATAL);
2054 					goto cleanup;
2055 				}
2056 				fi = irdflds->fi;
2057 				allocated_size = irdflds->allocated;
2058 			}
2059 
2060 			/*
2061 			 * copy any other fields (if there are any) up past the
2062 			 * expansion
2063 			 */
2064 			for (j = irdflds->nfields - 1; j > i; j--)
2065 			{
2066 				MYLOG(0, "copying field %d to %d\n", j, increased_cols + j);
2067 				fi[increased_cols + j] = fi[j];
2068 			}
2069 			MYLOG(0, "done copying fields\n");
2070 
2071 			/* Set the new number of fields */
2072 			irdflds->nfields += increased_cols;
2073 			MYLOG(0, "irdflds->nfields now at %d\n", irdflds->nfields);
2074 
2075 
2076 			/* copy the new field info */
2077 			do_all_tables = (wfi->ti ? FALSE : TRUE);
2078 			wfi = NULL;
2079 
2080 			for (k = 0; k < (do_all_tables ? stmt->ntab : 1); k++)
2081 			{
2082 				TABLE_INFO *the_ti = do_all_tables ? ti[k] : fi[i]->ti;
2083 
2084 				cols = (Int2) QR_get_num_cached_tuples(the_ti->col_info->result);
2085 
2086 				for (n = 0; n < cols; n++)
2087 				{
2088 					FIELD_INFO	*afi;
2089 					BOOL		reuse = TRUE;
2090 
2091 					MYLOG(0, "creating field info: n=%d\n", n);
2092 					/* skip malloc (already did it for the Star) */
2093 					if (k > 0 || n > 0)
2094 					{
2095 						MYLOG(0, "allocating field info at %d\n", n + i);
2096 						fi[n + i] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
2097 						if (fi[n + i] == NULL)
2098 						{
2099 							SC_set_parse_status(stmt, STMT_PARSE_FATAL);
2100 							goto cleanup;
2101 						}
2102 						reuse = FALSE;
2103 					}
2104 					afi = fi[n + i];
2105 					/* Initialize the new space (or the * field) */
2106 					FI_Constructor(afi, reuse);
2107 					afi->ti = the_ti;
2108 
2109 					MYLOG(0, "about to copy at %d\n", n + i);
2110 
2111 					getColInfo(the_ti->col_info, afi, n);
2112 					afi->updatable = fupdatable;
2113 
2114 					MYLOG(0, "done copying\n");
2115 				}
2116 
2117 				i += cols;
2118 				MYLOG(0, "i now at %d\n", i);
2119 			}
2120 		}
2121 
2122 		/*
2123 		 * We either know which table the field was in because it was
2124 		 * qualified with a table name or alias -OR- there was only 1
2125 		 * table.
2126 		 */
2127 		else if (wfi->ti)
2128 		{
2129 			if (!searchColInfo(fi[i]->ti->col_info, wfi))
2130 			{
2131 				parse = FALSE;
2132 				wfi->updatable = FALSE;
2133 			}
2134 			i++;
2135 		}
2136 
2137 		/* Don't know the table -- search all tables in "from" list */
2138 		else
2139 		{
2140 			for (k = 0; k < stmt->ntab; k++)
2141 			{
2142 				if (searchColInfo(ti[k]->col_info, wfi))
2143 				{
2144 					wfi->ti = ti[k];	/* now know the table */
2145 					break;
2146 				}
2147 			}
2148 			if (k >= stmt->ntab)
2149 			{
2150 				parse = FALSE;
2151 				wfi->updatable = FALSE;
2152 			}
2153 			i++;
2154 		}
2155 	}
2156 
2157 	if (check_hasoids && updatable)
2158 		CheckPgClassInfo(stmt);
2159 	SC_set_parse_status(stmt, parse ? STMT_PARSE_COMPLETE : STMT_PARSE_INCOMPLETE);
2160 	for (i = 0; i < (int) irdflds->nfields; i++)
2161 	{
2162 		wfi = fi[i];
2163 		wfi->flag &= ~FIELD_PARSING;
2164 		if (0 != wfi->columntype || 0 != wfi->basetype)
2165 			wfi->flag |= FIELD_PARSED_OK;
2166 	}
2167 
2168 	if (updatable)
2169 	{
2170 		if (stmt->ntab > 1)
2171 			updatable = FALSE;
2172 	}
2173 	SC_set_updatable(stmt, updatable);
2174 cleanup:
2175 #undef	return
2176 	if (!sqlsvr_check && STMT_PARSE_FATAL == SC_parsed_status(stmt))
2177 	{
2178 		SC_initialize_cols_info(stmt, FALSE, FALSE);
2179 		parse = FALSE;
2180 	}
2181 
2182 	MYLOG(0, "laving parse=%d, parse_status=%d\n", parse, SC_parsed_status(stmt));
2183 	return parse;
2184 }
2185 
2186 char
parse_statement(StatementClass * stmt,BOOL check_hasoids)2187 parse_statement(StatementClass *stmt, BOOL check_hasoids)
2188 {
2189 	return parse_the_statement(stmt, check_hasoids, FALSE);
2190 }
2191 
2192 char
parse_sqlsvr(StatementClass * stmt)2193 parse_sqlsvr(StatementClass *stmt)
2194 {
2195 	return parse_the_statement(stmt, FALSE, TRUE);
2196 }
2197