parse.c 20.4 KB
Newer Older
1 2
/*--------
 * Module:			parse.c
Byron Nikolaidis's avatar
Byron Nikolaidis committed
3
 *
4 5
 * Description:		This module contains routines related to parsing SQL
 *					statements.  This can be useful for two reasons:
Byron Nikolaidis's avatar
Byron Nikolaidis committed
6
 *
7 8
 *					1. So the query does not actually have to be executed
 *					to return data about it
Byron Nikolaidis's avatar
Byron Nikolaidis committed
9
 *
10 11 12 13 14
 *					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.
Byron Nikolaidis's avatar
Byron Nikolaidis committed
15
 *
16
 * Classes:			none
Byron Nikolaidis's avatar
Byron Nikolaidis committed
17
 *
18
 * API functions:	none
Byron Nikolaidis's avatar
Byron Nikolaidis committed
19
 *
20
 * Comments:		See "notice.txt" for copyright and license information.
21
 *--------
Byron Nikolaidis's avatar
Byron Nikolaidis committed
22
 */
Hiroshi Inoue's avatar
Hiroshi Inoue committed
23 24
/* Multibyte support	Eiji Tokuya 2001-03-15 */

25
#include "psqlodbc.h"
Byron Nikolaidis's avatar
Byron Nikolaidis committed
26 27 28

#include <stdio.h>
#include <string.h>
29
#include <ctype.h>
Byron Nikolaidis's avatar
Byron Nikolaidis committed
30 31 32 33 34

#include "statement.h"
#include "connection.h"
#include "qresult.h"
#include "pgtypes.h"
35
#include "pgapifunc.h"
Byron Nikolaidis's avatar
Byron Nikolaidis committed
36

Hiroshi Inoue's avatar
Hiroshi Inoue committed
37 38 39 40
#ifdef MULTIBYTE
#include "multibyte.h"
#endif

41 42
#ifndef WIN32
#ifndef HAVE_STRICMP
43
#define stricmp(s1,s2)		strcasecmp(s1,s2)
Byron Nikolaidis's avatar
Byron Nikolaidis committed
44 45 46 47 48 49 50 51
#define strnicmp(s1,s2,n)	strncasecmp(s1,s2,n)
#endif
#endif

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

52 53 54
char	   *getNextToken(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);
55

56

Byron Nikolaidis's avatar
Byron Nikolaidis committed
57 58 59
char *
getNextToken(char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric)
{
60 61 62 63
	int			i = 0;
	int			out = 0;
	char		qc,
				in_escape = FALSE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
64 65 66 67 68 69 70

	if (smax <= 1)
		return NULL;

	smax--;

	/* skip leading delimiters */
71 72
	while (isspace((unsigned char) s[i]) || s[i] == ',')
	{
73
		/* mylog("skipping '%c'\n", s[i]); */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
74 75 76
		i++;
	}

77
	if (s[i] == '\0')
78
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
79 80 81 82
		token[0] = '\0';
		return NULL;
	}

83 84 85 86 87 88
	if (quote)
		*quote = FALSE;
	if (dquote)
		*dquote = FALSE;
	if (numeric)
		*numeric = FALSE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
89 90

	/* get the next token */
91 92 93
	while (!isspace((unsigned char) s[i]) && s[i] != ',' &&
		   s[i] != '\0' && out != smax)
	{
94 95 96 97 98 99 100
#ifdef MULTIBYTE
		if (multibyte_char_check(s[i]) != 0)
		{
			token[out++] = s[i++];
			continue;
		}
#endif			
101 102 103
		/* Handle quoted stuff */
		if (out == 0 && (s[i] == '\"' || s[i] == '\''))
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
104
			qc = s[i];
105 106 107 108
			if (qc == '\"')
			{
				if (dquote)
					*dquote = TRUE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
109
			}
110 111 112 113
			if (qc == '\'')
			{
				if (quote)
					*quote = TRUE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
114 115
			}

116 117 118
			i++;				/* dont return the quote */
			while (s[i] != '\0' && out != smax)
			{
Hiroshi Inoue's avatar
Hiroshi Inoue committed
119
#ifdef MULTIBYTE
120
				if (multibyte_char_check(s[i]) != 0)
121
				{
122 123 124 125 126 127
					token[out++] = s[i++];
					continue;
				}
#endif			
				if (s[i] == qc && !in_escape)
					break;
128 129
				if (s[i] == '\\' && !in_escape)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
130
					in_escape = TRUE;
131
				}
132 133 134
				else
				{
					in_escape = FALSE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
135 136 137 138 139 140 141 142 143
					token[out++] = s[i];
				}
				i++;
			}
			if (s[i] == qc)
				i++;
			break;
		}

144 145 146 147 148
		/* Check for numeric literals */
		if (out == 0 && isdigit((unsigned char) s[i]))
		{
			if (numeric)
				*numeric = TRUE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
149
			token[out++] = s[i++];
150
			while (isalnum((unsigned char) s[i]) || s[i] == '.')
Byron Nikolaidis's avatar
Byron Nikolaidis committed
151 152 153 154 155
				token[out++] = s[i++];

			break;
		}

156 157
		if (ispunct((unsigned char) s[i]) && s[i] != '_')
		{
158 159
			mylog("got ispunct: s[%d] = '%c'\n", i, s[i]);

160 161
			if (out == 0)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
162 163 164 165 166 167 168 169 170 171 172 173 174
				token[out++] = s[i++];
				break;
			}
			else
				break;
		}

		if (out != smax)
			token[out++] = s[i];

		i++;
	}

175
	/* mylog("done -- s[%d] = '%c'\n", i, s[i]); */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
176 177 178

	token[out] = '\0';

179 180
	/* find the delimiter  */
	while (isspace((unsigned char) s[i]))
Byron Nikolaidis's avatar
Byron Nikolaidis committed
181 182
		i++;

183 184 185 186 187
	/* return the most priority delimiter */
	if (s[i] == ',')
	{
		if (delim)
			*delim = s[i];
Byron Nikolaidis's avatar
Byron Nikolaidis committed
188
	}
189 190 191 192
	else if (s[i] == '\0')
	{
		if (delim)
			*delim = '\0';
Byron Nikolaidis's avatar
Byron Nikolaidis committed
193
	}
194 195 196 197
	else
	{
		if (delim)
			*delim = ' ';
Byron Nikolaidis's avatar
Byron Nikolaidis committed
198 199 200
	}

	/* skip trailing blanks  */
201
	while (isspace((unsigned char) s[i]))
Byron Nikolaidis's avatar
Byron Nikolaidis committed
202 203 204 205 206 207
		i++;

	return &s[i];
}


208
#if 0
Byron Nikolaidis's avatar
Byron Nikolaidis committed
209 210 211 212 213 214 215 216 217 218 219 220 221
QR_set_num_fields(stmt->result, 14);
QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 4, "DATA_TYPE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
QR_set_field_info(stmt->result, 6, "PRECISION", PG_TYPE_INT4, 4);
QR_set_field_info(stmt->result, 7, "LENGTH", PG_TYPE_INT4, 4);
QR_set_field_info(stmt->result, 8, "SCALE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 9, "RADIX", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254);
222
/*	User defined fields */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
223 224
QR_set_field_info(stmt->result, 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4);
QR_set_field_info(stmt->result, 13, "FIELD_TYPE", PG_TYPE_INT4, 4);
225
#endif
Byron Nikolaidis's avatar
Byron Nikolaidis committed
226 227

void
Bruce Momjian's avatar
Bruce Momjian committed
228
getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k)
Byron Nikolaidis's avatar
Byron Nikolaidis committed
229
{
230
	char *str;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
231 232 233
	if (fi->name[0] == '\0')
		strcpy(fi->name, QR_get_value_manual(col_info->result, k, 3));

234 235 236
	fi->type = atoi(QR_get_value_manual(col_info->result, k, 13));
	fi->precision = atoi(QR_get_value_manual(col_info->result, k, 6));
	fi->length = atoi(QR_get_value_manual(col_info->result, k, 7));
237 238 239 240
	if (str = QR_get_value_manual(col_info->result, k, 8), str)
		fi->scale = atoi(str);
	else
		fi->scale = -1;
241 242
	fi->nullable = atoi(QR_get_value_manual(col_info->result, k, 10));
	fi->display_size = atoi(QR_get_value_manual(col_info->result, k, 12));
Byron Nikolaidis's avatar
Byron Nikolaidis committed
243 244
}

245

Byron Nikolaidis's avatar
Byron Nikolaidis committed
246
char
Bruce Momjian's avatar
Bruce Momjian committed
247
searchColInfo(COL_INFO *col_info, FIELD_INFO *fi)
Byron Nikolaidis's avatar
Byron Nikolaidis committed
248
{
249 250 251
	int			k,
				cmp;
	char	   *col;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
252

253 254
	for (k = 0; k < QR_get_num_tuples(col_info->result); k++)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
255
		col = QR_get_value_manual(col_info->result, k, 3);
256 257 258 259 260
		if (fi->dquote)
			cmp = strcmp(col, fi->name);
		else
			cmp = stricmp(col, fi->name);
		if (!cmp)
261
		{
262 263
			if (!fi->dquote)
				strcpy(fi->name, col);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
264 265 266 267 268 269 270 271 272 273 274 275
			getColInfo(col_info, fi, k);

			mylog("PARSE: searchColInfo: \n");
			return TRUE;
		}
	}

	return FALSE;
}


char
Bruce Momjian's avatar
Bruce Momjian committed
276
parse_statement(StatementClass *stmt)
Byron Nikolaidis's avatar
Byron Nikolaidis committed
277
{
278 279 280 281 282 283 284
	static char *func = "parse_statement";
	char		token[256];
	char		delim,
				quote,
				dquote,
				numeric,
				unquoted;
285
	char	   *ptr, *pptr = NULL;
286 287 288 289
	char		in_select = FALSE,
				in_distinct = FALSE,
				in_on = FALSE,
				in_from = FALSE,
290
				from_found = FALSE,
291 292 293 294 295 296 297 298 299 300 301
				in_where = FALSE,
				in_table = FALSE;
	char		in_field = FALSE,
				in_expr = FALSE,
				in_func = FALSE,
				in_dot = FALSE,
				in_as = FALSE;
	int			j,
				i,
				k = 0,
				n,
302
				first_where = 0,
303 304 305 306 307 308 309 310
				blevel = 0;
	FIELD_INFO **fi;
	TABLE_INFO **ti;
	char		parse;
	ConnectionClass *conn = stmt->hdbc;
	HSTMT		hcol_stmt;
	StatementClass *col_stmt;
	RETCODE		result;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
311

312 313
	mylog("%s: entering...\n", func);

Byron Nikolaidis's avatar
Byron Nikolaidis committed
314 315 316 317 318 319 320
	ptr = stmt->statement;
	fi = stmt->fi;
	ti = stmt->ti;

	stmt->nfld = 0;
	stmt->ntab = 0;

321 322 323 324
#ifdef MULTIBYTE
	multibyte_init();
#endif
	while (pptr = ptr, (ptr = getNextToken(pptr, token, sizeof(token), &delim, &quote, &dquote, &numeric)) != NULL)
325 326
	{
		unquoted = !(quote || dquote);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
327 328 329

		mylog("unquoted=%d, quote=%d, dquote=%d, numeric=%d, delim='%c', token='%s', ptr='%s'\n", unquoted, quote, dquote, numeric, delim, token, ptr);

330
		if (in_select && unquoted && blevel == 0)
331
		{
332 333 334
			if (!stricmp(token, "distinct"))
			{
				in_distinct = TRUE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
335

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
				mylog("DISTINCT\n");
				continue;
			}
			if (!stricmp(token, "into"))
			{
				in_select = FALSE;
				mylog("INTO\n");
				stmt->statement_type = STMT_TYPE_CREATE;
				stmt->parse_status = STMT_PARSE_FATAL;
				return FALSE;
			}
			if (!stricmp(token, "from"))
			{
				in_select = FALSE;
				in_from = TRUE;
				if (!from_found &&
				    (!strnicmp(pptr, "from", 4)))
				{
					mylog("First ");
					from_found = TRUE;
				}
Byron Nikolaidis's avatar
Byron Nikolaidis committed
357

358 359 360
				mylog("FROM\n");
				continue;
			}
Byron Nikolaidis's avatar
Byron Nikolaidis committed
361
		}
362
		if (unquoted && blevel == 0)
363
		{
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
			if ((!stricmp(token, "where") ||
				!stricmp(token, "union") ||
				!stricmp(token, "intersect") ||
				!stricmp(token, "except") ||
				!stricmp(token, "order") ||
				!stricmp(token, "group") ||
				!stricmp(token, "having")))
			{
				in_select = FALSE;
				in_from = FALSE;
				in_where = TRUE;

				if (!first_where &&
				    (!stricmp(token, "where")))
					first_where = ptr - stmt->statement;
				    
				mylog("WHERE...\n");
				break;
			}
Byron Nikolaidis's avatar
Byron Nikolaidis committed
383
		}
384
		if (in_select && (in_expr || in_func))
385
		{
386 387 388 389
			/* just eat the expression */
			mylog("in_expr=%d or func=%d\n", in_expr, in_func);
			if (!unquoted)
				continue;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
390

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
			if (token[0] == '(')
			{
				blevel++;
				mylog("blevel++ = %d\n", blevel);
			}
			else if (token[0] == ')')
			{
				blevel--;
				mylog("blevel-- = %d\n", blevel);
			}
			if (blevel == 0)
			{
				if (delim == ',')
				{
					mylog("**** Got comma in_expr/func\n");
					in_func = FALSE;
					in_expr = FALSE;
					in_field = FALSE;
				}
				else if (!stricmp(token, "as"))
				{
					mylog("got AS in_expr\n");
					in_func = FALSE;
					in_expr = FALSE;
					in_as = TRUE;
					in_field = TRUE;
				}
			}
Byron Nikolaidis's avatar
Byron Nikolaidis committed
419 420 421
			continue;
		}

422
		if (unquoted && !stricmp(token, "select"))
423
		{
424
			in_select = TRUE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
425

426 427
			mylog("SELECT\n");
			continue;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
428
		}
429 430 431 432
		if (in_select)
		{
			if (in_distinct)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
433 434
				mylog("in distinct\n");

435 436
				if (unquoted && !stricmp(token, "on"))
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
437 438 439 440
					in_on = TRUE;
					mylog("got on\n");
					continue;
				}
441 442
				if (in_on)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
443 444
					in_distinct = FALSE;
					in_on = FALSE;
445
					continue;	/* just skip the unique on field */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
446 447 448 449 450
				}
				mylog("done distinct\n");
				in_distinct = FALSE;
			}

451 452 453
			if (!in_field)
			{
				if (!token[0])
Byron Nikolaidis's avatar
Byron Nikolaidis committed
454 455
					continue;

456 457
				if (!(stmt->nfld % FLD_INCR))
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
458 459
					mylog("reallocing at nfld=%d\n", stmt->nfld);
					fi = (FIELD_INFO **) realloc(fi, (stmt->nfld + FLD_INCR) * sizeof(FIELD_INFO *));
460 461
					if (!fi)
					{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
462 463 464 465 466 467
						stmt->parse_status = STMT_PARSE_FATAL;
						return FALSE;
					}
					stmt->fi = fi;
				}

468 469 470
				fi[stmt->nfld] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
				if (fi[stmt->nfld] == NULL)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
471 472 473 474
					stmt->parse_status = STMT_PARSE_FATAL;
					return FALSE;
				}

475
				/* Initialize the field info */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
476 477
				memset(fi[stmt->nfld], 0, sizeof(FIELD_INFO));

478
				/* double quotes are for qualifiers */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
479 480 481
				if (dquote)
					fi[stmt->nfld]->dquote = TRUE;

482 483
				if (quote)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
484
					fi[stmt->nfld++]->quote = TRUE;
485
in_expr = TRUE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
486 487
					continue;
				}
488 489
				else if (numeric)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
490 491 492
					mylog("**** got numeric: nfld = %d\n", stmt->nfld);
					fi[stmt->nfld]->numeric = TRUE;
				}
493 494
				else if (token[0] == '(')
				{				/* expression */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
495 496 497 498 499 500
					mylog("got EXPRESSION\n");
					fi[stmt->nfld++]->expr = TRUE;
					in_expr = TRUE;
					blevel = 1;
					continue;
				}
501 502
				else
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
503 504 505 506 507
					strcpy(fi[stmt->nfld]->name, token);
					fi[stmt->nfld]->dot[0] = '\0';
				}
				mylog("got field='%s', dot='%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->dot);

508
				if (delim == ',')
Byron Nikolaidis's avatar
Byron Nikolaidis committed
509
					mylog("comma (1)\n");
510
				else
Byron Nikolaidis's avatar
Byron Nikolaidis committed
511 512 513 514 515
					in_field = TRUE;
				stmt->nfld++;
				continue;
			}

516 517 518
			/*
			 * We are in a field now
			 */
519 520
			if (in_dot)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
521 522 523 524 525 526
				stmt->nfld--;
				strcpy(fi[stmt->nfld]->dot, fi[stmt->nfld]->name);
				strcpy(fi[stmt->nfld]->name, token);
				stmt->nfld++;
				in_dot = FALSE;

527 528
				if (delim == ',')
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
529 530 531 532 533 534
					mylog("in_dot: got comma\n");
					in_field = FALSE;
				}
				continue;
			}

535 536
			if (in_as)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
537 538 539 540 541 542 543 544
				stmt->nfld--;
				strcpy(fi[stmt->nfld]->alias, token);
				mylog("alias for field '%s' is '%s'\n", fi[stmt->nfld]->name, fi[stmt->nfld]->alias);
				in_as = FALSE;
				in_field = FALSE;

				stmt->nfld++;

545
				if (delim == ',')
Byron Nikolaidis's avatar
Byron Nikolaidis committed
546 547 548 549
					mylog("comma(2)\n");
				continue;
			}

550 551 552
			/* Function */
			if (token[0] == '(')
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
553 554
				in_func = TRUE;
				blevel = 1;
555 556 557 558 559 560 561
				fi[stmt->nfld - 1]->func = TRUE;

				/*
				 * name will have the function name -- maybe useful some
				 * day
				 */
				mylog("**** got function = '%s'\n", fi[stmt->nfld - 1]->name);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
562 563 564
				continue;
			}

565 566 567
			if (token[0] == '.')
			{
				in_dot = TRUE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
568 569 570 571
				mylog("got dot\n");
				continue;
			}

572 573
			if (!stricmp(token, "as"))
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
574 575 576 577 578
				in_as = TRUE;
				mylog("got AS\n");
				continue;
			}

579
			/* otherwise, it's probably an expression */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
580
			in_expr = TRUE;
581 582
			fi[stmt->nfld - 1]->expr = TRUE;
			fi[stmt->nfld - 1]->name[0] = '\0';
Byron Nikolaidis's avatar
Byron Nikolaidis committed
583 584 585
			mylog("*** setting expression\n");
		}

586 587 588 589 590
		if (in_from)
		{
			if (!in_table)
			{
				if (!token[0])
Byron Nikolaidis's avatar
Byron Nikolaidis committed
591 592
					continue;

593 594
				if (!(stmt->ntab % TAB_INCR))
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
595
					ti = (TABLE_INFO **) realloc(ti, (stmt->ntab + TAB_INCR) * sizeof(TABLE_INFO *));
596 597
					if (!ti)
					{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
598 599 600 601 602 603
						stmt->parse_status = STMT_PARSE_FATAL;
						return FALSE;
					}
					stmt->ti = ti;
				}
				ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO));
604 605
				if (ti[stmt->ntab] == NULL)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
606 607 608 609 610 611 612
					stmt->parse_status = STMT_PARSE_FATAL;
					return FALSE;
				}

				ti[stmt->ntab]->alias[0] = '\0';

				strcpy(ti[stmt->ntab]->name, token);
613 614
				if (!dquote)
				{
615 616
					char	   *ptr;

617 618 619 620
					/* lower case table name */
					for (ptr = ti[stmt->ntab]->name; *ptr; ptr++)
						*ptr = tolower((unsigned char) *ptr);
				}
Byron Nikolaidis's avatar
Byron Nikolaidis committed
621 622
				mylog("got table = '%s'\n", ti[stmt->ntab]->name);

623
				if (delim == ',')
Byron Nikolaidis's avatar
Byron Nikolaidis committed
624
					mylog("more than 1 tables\n");
625
				else
Byron Nikolaidis's avatar
Byron Nikolaidis committed
626 627 628 629 630
					in_table = TRUE;
				stmt->ntab++;
				continue;
			}

631 632
			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);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
633
			in_table = FALSE;
634
			if (delim == ',')
Byron Nikolaidis's avatar
Byron Nikolaidis committed
635 636 637 638
				mylog("more than 1 tables\n");
		}
	}

639 640 641
	/*
	 * Resolve any possible field names with tables
	 */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
642 643 644

	parse = TRUE;

645 646 647 648 649
	/* Resolve field names with tables */
	for (i = 0; i < stmt->nfld; i++)
	{
		if (fi[i]->func || fi[i]->expr || fi[i]->numeric)
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
650 651 652 653 654
			fi[i]->ti = NULL;
			fi[i]->type = -1;
			parse = FALSE;
			continue;
		}
655 656
		else if (fi[i]->quote)
		{						/* handle as text */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
657
			fi[i]->ti = NULL;
658
			/*
Byron Nikolaidis's avatar
Byron Nikolaidis committed
659 660
			fi[i]->type = PG_TYPE_TEXT;
			fi[i]->precision = 0;
661 662 663
			the following may be better */
			fi[i]->type = PG_TYPE_UNKNOWN;
			fi[i]->precision = 254;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
664
			continue;
665 666 667 668 669 670 671 672
		}
		/* it's a dot, resolve to table or alias */
		else if (fi[i]->dot[0])
		{
			for (k = 0; k < stmt->ntab; k++)
			{
				if (!stricmp(ti[k]->name, fi[i]->dot))
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
673 674 675
					fi[i]->ti = ti[k];
					break;
				}
676 677
				else if (!stricmp(ti[k]->alias, fi[i]->dot))
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
678 679 680 681 682 683 684 685 686 687 688 689
					fi[i]->ti = ti[k];
					break;
				}
			}
		}
		else if (stmt->ntab == 1)
			fi[i]->ti = ti[0];
	}

	mylog("--------------------------------------------\n");
	mylog("nfld=%d, ntab=%d\n", stmt->nfld, stmt->ntab);

690 691
	for (i = 0; i < stmt->nfld; i++)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
692 693
		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)
694
			mylog("     ----> table_name='%s', table_alias='%s'\n", fi[i]->ti->name, fi[i]->ti->alias);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
695 696
	}

697
	for (i = 0; i < stmt->ntab; i++)
Byron Nikolaidis's avatar
Byron Nikolaidis committed
698 699 700
		mylog("Table %d: name='%s', alias='%s'\n", i, ti[i]->name, ti[i]->alias);


701 702 703
	/*
	 * Now save the SQLColumns Info for the parse tables
	 */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
704

705 706 707 708 709
	/* Call SQLColumns for each table and store the result */
	for (i = 0; i < stmt->ntab; i++)
	{
		/* See if already got it */
		char		found = FALSE;
710

711 712 713 714
		for (k = 0; k < conn->ntables; k++)
		{
			if (!stricmp(conn->col_info[k]->name, ti[i]->name))
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
715 716 717 718 719
				mylog("FOUND col_info table='%s'\n", ti[i]->name);
				found = TRUE;
				break;
			}
		}
720 721 722

		if (!found)
		{
723
			mylog("PARSE: Getting PG_Columns for table[%d]='%s'\n", i, ti[i]->name);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
724

725
			result = PGAPI_AllocStmt(stmt->hdbc, &hcol_stmt);
726 727
			if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO))
			{
728
				stmt->errormsg = "PGAPI_AllocStmt failed in parse_statement for columns.";
Byron Nikolaidis's avatar
Byron Nikolaidis committed
729 730 731 732 733 734 735 736
				stmt->errornumber = STMT_NO_MEMORY_ERROR;
				stmt->parse_status = STMT_PARSE_FATAL;
				return FALSE;
			}

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

737
			result = PGAPI_Columns(hcol_stmt, "", 0, "", 0,
Byron Nikolaidis's avatar
Byron Nikolaidis committed
738
						ti[i]->name, (SWORD) strlen(ti[i]->name), "", 0);
739

740
			mylog("        Past PG_Columns\n");
741 742
			if (result == SQL_SUCCESS)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
743
				mylog("      Success\n");
744 745
				if (!(conn->ntables % COL_INCR))
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
746 747 748
					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 *));
749 750
					if (!conn->col_info)
					{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
751 752 753 754 755 756 757
						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));
758 759
				if (!conn->col_info[conn->ntables])
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
760 761 762 763
					stmt->parse_status = STMT_PARSE_FATAL;
					return FALSE;
				}

764 765 766 767
				/*
				 * Store the table name and the SQLColumns result
				 * structure
				 */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
768 769 770
				strcpy(conn->col_info[conn->ntables]->name, ti[i]->name);
				conn->col_info[conn->ntables]->result = col_stmt->result;

771 772 773 774
				/*
				 * The connection will now free the result structures, so
				 * make sure that the statement doesn't free it
				 */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
775 776 777 778
				col_stmt->result = NULL;

				conn->ntables++;

779
				PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
780 781
				mylog("Created col_info table='%s', ntables=%d\n", ti[i]->name, conn->ntables);
			}
782 783
			else
			{
784
				PGAPI_FreeStmt(hcol_stmt, SQL_DROP);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
785 786 787 788
				break;
			}
		}

789
		/* Associate a table from the statement with a SQLColumn info */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
790 791 792 793
		ti[i]->col_info = conn->col_info[k];
		mylog("associate col_info: i=%d, k=%d\n", i, k);
	}

794
	mylog("Done PG_Columns\n");
Byron Nikolaidis's avatar
Byron Nikolaidis committed
795

796 797 798
	/*
	 * Now resolve the fields to point to column info
	 */
799 800 801 802 803
	for (i = 0; i < stmt->nfld;)
	{
		/* Dont worry about functions or quotes */
		if (fi[i]->func || fi[i]->quote || fi[i]->numeric)
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
804 805 806 807
			i++;
			continue;
		}

808 809 810 811 812 813 814 815 816
		/* 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;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
817 818 819

			mylog("expanding field %d\n", i);

820
			total_cols = 0;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
821

822 823
			if (fi[i]->ti)		/* The star represents only the qualified
								 * table */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
824 825
				total_cols = QR_get_num_tuples(fi[i]->ti->col_info->result);

826 827
			else
			{					/* The star represents all tables */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
828

829 830
				/* Calculate the total number of columns after expansion */
				for (k = 0; k < stmt->ntab; k++)
Byron Nikolaidis's avatar
Byron Nikolaidis committed
831 832
					total_cols += QR_get_num_tuples(ti[k]->col_info->result);
			}
833
			increased_cols = total_cols - 1;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
834

835 836
			/* Allocate some more field pointers if necessary */
			old_alloc = ((stmt->nfld - 1) / FLD_INCR + 1) * FLD_INCR;
837
			new_size = stmt->nfld + increased_cols;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
838

839 840 841 842 843
			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;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
844

845 846
				mylog("need more cols: new_alloc = %d\n", new_alloc);
				fi = (FIELD_INFO **) realloc(fi, new_alloc * sizeof(FIELD_INFO *));
847 848
				if (!fi)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
849 850 851
					stmt->parse_status = STMT_PARSE_FATAL;
					return FALSE;
				}
852
				stmt->fi = fi;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
853 854
			}

855 856 857 858 859 860
			/*
			 * copy any other fields (if there are any) up past the
			 * expansion
			 */
			for (j = stmt->nfld - 1; j > i; j--)
			{
861 862
				mylog("copying field %d to %d\n", j, increased_cols + j);
				fi[increased_cols + j] = fi[j];
Byron Nikolaidis's avatar
Byron Nikolaidis committed
863 864 865
			}
			mylog("done copying fields\n");

866
			/* Set the new number of fields */
867
			stmt->nfld += increased_cols;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
868 869 870
			mylog("stmt->nfld now at %d\n", stmt->nfld);


871
			/* copy the new field info */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
872 873
			do_all_tables = (fi[i]->ti ? FALSE : TRUE);

874 875
			for (k = 0; k < (do_all_tables ? stmt->ntab : 1); k++)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
876 877 878 879
				TABLE_INFO *the_ti = do_all_tables ? ti[k] : fi[i]->ti;

				cols = QR_get_num_tuples(the_ti->col_info->result);

880 881
				for (n = 0; n < cols; n++)
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
882
					mylog("creating field info: n=%d\n", n);
883
					/* skip malloc (already did it for the Star) */
884 885
					if (k > 0 || n > 0)
					{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
886
						mylog("allocating field info at %d\n", n + i);
887 888 889
						fi[n + i] = (FIELD_INFO *) malloc(sizeof(FIELD_INFO));
						if (fi[n + i] == NULL)
						{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
890 891 892 893
							stmt->parse_status = STMT_PARSE_FATAL;
							return FALSE;
						}
					}
894
					/* Initialize the new space (or the * field) */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
					memset(fi[n + i], 0, sizeof(FIELD_INFO));
					fi[n + i]->ti = the_ti;

					mylog("about to copy at %d\n", n + i);

					getColInfo(the_ti->col_info, fi[n + i], n);

					mylog("done copying\n");
				}

				i += cols;
				mylog("i now at %d\n", i);
			}
		}

910 911 912 913 914 915 916 917
		/*
		 * 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]))
Byron Nikolaidis's avatar
Byron Nikolaidis committed
918 919 920 921 922
				parse = FALSE;

			i++;
		}

923 924 925
		/* Don't know the table -- search all tables in "from" list */
		else
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
926
			parse = FALSE;
927 928 929 930
			for (k = 0; k < stmt->ntab; k++)
			{
				if (searchColInfo(ti[k]->col_info, fi[i]))
				{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
931 932 933 934 935 936 937 938 939
					fi[i]->ti = ti[k];	/* now know the table */
					parse = TRUE;
					break;
				}
			}
			i++;
		}
	}

940
	if (!parse)
Byron Nikolaidis's avatar
Byron Nikolaidis committed
941 942 943 944 945 946 947
		stmt->parse_status = STMT_PARSE_INCOMPLETE;
	else
		stmt->parse_status = STMT_PARSE_COMPLETE;

	mylog("done parse_statement: parse=%d, parse_status=%d\n", parse, stmt->parse_status);
	return parse;
}