parse.c

来自「postgresql-odbc,跨平台应用」· C语言 代码 · 共 1,872 行 · 第 1/3 页

C
1,872
字号
/*-------- * Module:			parse.c * * Description:		This module contains routines related to parsing SQL *					statements.  This can be useful for two reasons: * *					1. So the query does not actually have to be executed *					to return data about it * *					2. To be able to return information about precision, *					nullability, aliases, etc. in the functions *					SQLDescribeCol and SQLColAttributes.  Currently, *					Postgres doesn't return any information about *					these things in a query. * * Classes:			none * * API functions:	none * * Comments:		See "notice.txt" for copyright and license information. *-------- *//* Multibyte support	Eiji Tokuya 2001-03-15 */#include "psqlodbc.h"#include <stdio.h>#include <string.h>#include <ctype.h>#include "statement.h"#include "connection.h"#include "qresult.h"#include "pgtypes.h"#include "pgapifunc.h"#include "catfunc.h"#include "multibyte.h"#define FLD_INCR	32#define TAB_INCR	8#define COLI_INCR	16static	char	*getNextToken(int ccsc, char escape_in_literal, char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);static	void	getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k);static	char	searchColInfo(COL_INFO *col_info, FIELD_INFO *fi);Int4 FI_precision(const FIELD_INFO *fi){	OID	ftype;	if (!fi)	return -1;	ftype = FI_type(fi);	switch (ftype)	{		case PG_TYPE_NUMERIC:			return fi->column_size;		case PG_TYPE_DATETIME:		case PG_TYPE_TIMESTAMP_NO_TMZONE:			return fi->decimal_digits;	}	return 0;}Int4 FI_scale(const FIELD_INFO *fi){	OID	ftype;	if (!fi)	return -1;	ftype = FI_type(fi);	switch (ftype)	{		case PG_TYPE_NUMERIC:			return fi->decimal_digits;	}	return 0;}static char *getNextToken(	int ccsc, /* client encoding */	char escape_ch,	char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric){	int			i = 0;	int		out = 0, taglen;	char		qc, in_quote, in_dollar_quote, in_escape;	const	char	*tag, *tagend;	encoded_str	encstr;	char	literal_quote = LITERAL_QUOTE, identifier_quote = IDENTIFIER_QUOTE, dollar_quote = DOLLAR_QUOTE, escape_in_literal;	if (smax <= 1)		return NULL;	smax--;	/* skip leading delimiters */	while (isspace((UCHAR) s[i]) || s[i] == ',')	{		/* mylog("skipping '%c'\n", s[i]); */		i++;	}	if (s[i] == '\0')	{		token[0] = '\0';		return NULL;	}	if (quote)		*quote = FALSE;	if (dquote)		*dquote = FALSE;	if (numeric)		*numeric = FALSE;	encoded_str_constr(&encstr, ccsc, &s[i]);	/* get the next token */	while (s[i] != '\0' && out < smax)	{		encoded_nextchar(&encstr);		if (ENCODE_STATUS(encstr) != 0)		{			token[out++] = s[i++];			continue;		}		if (isspace((UCHAR) s[i]) || s[i] == ',')			break;		/* Handle quoted stuff */		in_quote = in_dollar_quote = FALSE;		taglen = 0;		tag = NULL;		escape_in_literal = '\0';		if (out == 0)		{			qc = s[i];			if (qc == dollar_quote)			{				in_quote = in_dollar_quote = TRUE;				tag = s + i;				taglen = 1;				if (tagend = strchr(s + i + 1, dollar_quote), NULL != tagend)					taglen = tagend - s - i + 1;				i += (taglen - 1);				encoded_position_shift(&encstr, taglen - 1);				if (quote)					*quote = TRUE;			}			else if (qc == literal_quote)			{				in_quote = TRUE;				if (quote)					*quote = TRUE;				escape_in_literal = escape_ch;				if (!escape_in_literal)				{					if (LITERAL_EXT == s[i - 1])						escape_in_literal = ESCAPE_IN_LITERAL;				}			}			else if (qc == identifier_quote)			{				in_quote = TRUE;				if (dquote)					*dquote = TRUE;			}		}		if (in_quote)		{			i++;				/* dont return the quote */			in_escape = FALSE;			while (s[i] != '\0' && out != smax)			{				encoded_nextchar(&encstr);				if (ENCODE_STATUS(encstr) != 0)				{					token[out++] = s[i++];					continue;				}				if (in_escape)					in_escape = FALSE;				else if (s[i] == qc)				{					if (!in_dollar_quote)						break;					if (strncmp(s + i, tag, taglen) == 0)					{						i += (taglen - 1);						encoded_position_shift(&encstr, taglen - 1);						break;					}					token[out++] = s[i];				}				else if (literal_quote == qc && s[i] == escape_in_literal)				{					in_escape = TRUE;				}				else				{					token[out++] = s[i];				}				i++;			}			if (s[i] == qc)				i++;			break;		}		/* Check for numeric literals */		if (out == 0 && isdigit((UCHAR) s[i]))		{			if (numeric)				*numeric = TRUE;			token[out++] = s[i++];			while (isalnum((UCHAR) s[i]) || s[i] == '.')				token[out++] = s[i++];			break;		}		if (ispunct((UCHAR) s[i]) && s[i] != '_')		{			mylog("got ispunct: s[%d] = '%c'\n", i, s[i]);			if (out == 0)			{				token[out++] = s[i++];				break;			}			else				break;		}		if (out != smax)			token[out++] = s[i];		i++;	}	/* mylog("done -- s[%d] = '%c'\n", i, s[i]); */	token[out] = '\0';	/* find the delimiter  */	while (isspace((UCHAR) s[i]))		i++;	/* return the most priority delimiter */	if (s[i] == ',')	{		if (delim)			*delim = s[i];	}	else if (s[i] == '\0')	{		if (delim)			*delim = '\0';	}	else	{		if (delim)			*delim = ' ';	}	/* skip trailing blanks  */	while (isspace((UCHAR) s[i]))		i++;	return &s[i];}static voidgetColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k){	char	   *str;inolog("getColInfo non-manual result\n");	fi->dquote = TRUE;	STR_TO_NAME(fi->column_name, QR_get_value_backend_text(col_info->result, k, COLUMNS_COLUMN_NAME));	fi->columntype = (OID) QR_get_value_backend_int(col_info->result, k, COLUMNS_FIELD_TYPE, NULL);	fi->column_size = QR_get_value_backend_int(col_info->result, k, COLUMNS_PRECISION, NULL);	fi->length = QR_get_value_backend_int(col_info->result, k, COLUMNS_LENGTH, NULL);	if (str = QR_get_value_backend_text(col_info->result, k, COLUMNS_SCALE), str)		fi->decimal_digits = atoi(str);	else		fi->decimal_digits = -1;	fi->nullable = QR_get_value_backend_int(col_info->result, k, COLUMNS_NULLABLE, NULL);	fi->display_size = QR_get_value_backend_int(col_info->result, k, COLUMNS_DISPLAY_SIZE, NULL);	fi->auto_increment = QR_get_value_backend_int(col_info->result, k, COLUMNS_AUTO_INCREMENT, NULL);}static charsearchColInfo(COL_INFO *col_info, FIELD_INFO *fi){	int			k,				cmp, attnum;	const char	   *col;inolog("searchColInfo num_cols=%d col=%s\n", QR_get_num_cached_tuples(col_info->result), PRINT_NAME(fi->column_name));	if (fi->attnum < 0)		return FALSE;	for (k = 0; k < QR_get_num_cached_tuples(col_info->result); k++)	{		if (fi->attnum > 0)		{			attnum = QR_get_value_backend_int(col_info->result, k, COLUMNS_PHYSICAL_NUMBER, NULL);inolog("searchColInfo %d attnum=%d\n", k, attnum);			if (attnum == fi->attnum)			{				getColInfo(col_info, fi, k);				mylog("PARSE: searchColInfo by attnum=%d\n", attnum);				return TRUE;			}		}		else if (NAME_IS_VALID(fi->column_name))		{			col = QR_get_value_backend_text(col_info->result, k, COLUMNS_COLUMN_NAME);inolog("searchColInfo %d col=%s\n", k, col);			if (fi->dquote)				cmp = strcmp(col, GET_NAME(fi->column_name));			else				cmp = stricmp(col, GET_NAME(fi->column_name));			if (!cmp)			{				if (!fi->dquote)					STR_TO_NAME(fi->column_name, col);				getColInfo(col_info, fi, k);				mylog("PARSE: searchColInfo: \n");				return TRUE;			}		}	}	return FALSE;}/* *	lower the unquoted name */staticvoid lower_the_name(char *name, ConnectionClass *conn, BOOL dquote){	if (!dquote)	{		char		*ptr;		encoded_str	encstr;		make_encoded_str(&encstr, conn, name);		/* lower case table name */		for (ptr = name; *ptr; ptr++)		{			encoded_nextchar(&encstr);			if (ENCODE_STATUS(encstr) == 0)				*ptr = tolower((UCHAR) *ptr);		}	}}static BOOL CheckHasOids(StatementClass * stmt){	QResultClass	*res;	BOOL		hasoids = TRUE, foundKey = FALSE;	char		query[512];	ConnectionClass	*conn = SC_get_conn(stmt);	TABLE_INFO	*ti;	if (0 != SC_checked_hasoids(stmt))		return TRUE;	if (!stmt->ti || !stmt->ti[0])		return FALSE;	ti = stmt->ti[0];	sprintf(query, "select relhasoids, c.oid from pg_class c, pg_namespace n where relname = '%s' and nspname = '%s' and c.relnamespace = n.oid", SAFE_NAME(ti->table_name), SAFE_NAME(ti->schema_name));	res = CC_send_query(conn, query, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL);	if (QR_command_maybe_successful(res))	{		stmt->num_key_fields = PG_NUM_NORMAL_KEYS;		if (1 == QR_get_num_total_tuples(res))		{			const char *value = QR_get_value_backend_text(res, 0, 0);			if (value && ('f' == *value || '0' == *value))			{				hasoids = FALSE;				TI_set_has_no_oids(ti);			}			else			{				TI_set_hasoids(ti);				foundKey = TRUE;				STR_TO_NAME(ti->bestitem, OID_NAME);				sprintf(query, "\"%s\" = %u", OID_NAME);				STR_TO_NAME(ti->bestqual, query);			}			TI_set_hasoids_checked(ti);			ti->table_oid = (OID) strtoul(QR_get_value_backend_text(res, 0, 1), NULL, 10);		}		QR_Destructor(res);		res = NULL;		if (!hasoids)		{			sprintf(query, "select a.attname, a.atttypid from pg_index i, pg_attribute a where indrelid=%u and indnatts=1 and indisunique and indexprs is null and indpred is null and i.indrelid = a.attrelid and a.attnum=i.indkey[0] and attnotnull and atttypid in (%d, %d)", ti->table_oid, PG_TYPE_INT4, PG_TYPE_OID);			res = CC_send_query(conn, query, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL);			if (QR_command_maybe_successful(res) && QR_get_num_total_tuples(res) > 0)			{				foundKey = TRUE;				STR_TO_NAME(ti->bestitem, QR_get_value_backend_text(res, 0, 0));				sprintf(query, "\"%s\" = %%", SAFE_NAME(ti->bestitem));				if (PG_TYPE_INT4 == (OID) QR_get_value_backend_int(res, 0, 1, NULL))					strcat(query, "d");				else					strcat(query, "u");				STR_TO_NAME(ti->bestqual, query);			}			else			{				/* stmt->updatable = FALSE; */				foundKey = TRUE;				stmt->num_key_fields--;			}		}	}	QR_Destructor(res);	SC_set_checked_hasoids(stmt, foundKey); 	return TRUE;}	static BOOL increaseNtab(StatementClass *stmt, const char *func){	TABLE_INFO	**ti = stmt->ti, *wti;	if (!(stmt->ntab % TAB_INCR))	{		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);		stmt->ti = ti;	}	wti = ti[stmt->ntab] = (TABLE_INFO *) malloc(sizeof(TABLE_INFO));	if (wti == NULL)	{		SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for TABLE_INFO(2).", func);		return FALSE;	}	TI_Constructor(wti, SC_get_conn(stmt));	stmt->ntab++;	return TRUE;}	static void setNumFields(IRDFields *irdflds, size_t numFields){	FIELD_INFO	**fi = irdflds->fi;	size_t		nfields = irdflds->nfields;	if (numFields < nfields)	{		int	i;		for (i = (int) numFields; i < (int) nfields; i++)		{			if (fi[i])				fi[i]->flag = 0;		}	}	irdflds->nfields = (UInt4) numFields;}void SC_initialize_cols_info(StatementClass *stmt, BOOL DCdestroy, BOOL parseReset){	IRDFields	*irdflds = SC_get_IRDF(stmt);	/* Free the parsed table information */	if (stmt->ti)	{		TI_Destructor(stmt->ti, stmt->ntab);		free(stmt->ti);		stmt->ti = NULL;	}	stmt->ntab = 0;	if (DCdestroy) /* Free the parsed field information */		DC_Destructor((DescriptorClass *) SC_get_IRD(stmt));	else		setNumFields(irdflds, 0);	if (parseReset)	{		stmt->parse_status = STMT_PARSE_NONE;		stmt->updatable = FALSE;	}}static BOOL allocateFields(IRDFields *irdflds, size_t sizeRequested){	FIELD_INFO	**fi = irdflds->fi;	size_t		alloc_size, incr_size;	if (sizeRequested <= irdflds->allocated)		return TRUE;	alloc_size = (0 != irdflds->allocated ? irdflds->allocated : FLD_INCR);	for (; alloc_size < sizeRequested; alloc_size *= 2)			;	incr_size = sizeof(FIELD_INFO *) * (alloc_size - irdflds->allocated);	fi = (FIELD_INFO **) realloc(fi, alloc_size * sizeof(FIELD_INFO *));	if (!fi)	{		irdflds->fi = NULL;		irdflds->allocated = irdflds->nfields = 0;		return FALSE;	}	memset(&fi[irdflds->allocated], 0, incr_size);	irdflds->fi = fi;	irdflds->allocated = (SQLSMALLINT) alloc_size;	return TRUE;}static void xxxxx(FIELD_INFO *fi, QResultClass *res, int i){	STR_TO_NAME(fi->column_alias, QR_get_fieldname(res, i));	fi->basetype = QR_get_field_type(res, i);	if (0 == fi->columntype)		fi->columntype = fi->basetype;	if (fi->attnum < 0)	{		fi->nullable = FALSE;		fi->updatable = FALSE;	}	else if (fi->attnum > 0)		fi->nullable = TRUE;	/* probably ? */	if (NAME_IS_NULL(fi->column_name))	{		switch (fi->attnum)		{			case CTID_ATTNUM:				STR_TO_NAME(fi->column_name, "ctid");				break;			case OID_ATTNUM:				STR_TO_NAME(fi->column_name, OID_NAME);				break;			case XMIN_ATTNUM:				STR_TO_NAME(fi->column_name, "xmin");				break;		}	}}static has_multi_table(const StatementClass *stmt){	BOOL multi_table = FALSE;	QResultClass	*res;inolog("has_multi_table ntab=%d", stmt->ntab);	if (1 < stmt->ntab)		multi_table = TRUE;	else if (SC_has_join(stmt))		multi_table = TRUE;	else if (res = SC_get_Curres(stmt), NULL != res)	{		int	i, num_fields = QR_NumPublicResultCols(res);		OID	reloid = 0, greloid;		for (i = 0; i < num_fields; i++)		{			greloid = QR_get_relid(res, i);			if (0 != greloid)			{				if (0 == reloid)					reloid = greloid;				else if (reloid != greloid)				{inolog(" dohhhhhh");					multi_table = TRUE;					break;				}			}		}	}inolog(" multi=%d\n", multi_table);	return multi_table;}/* *	SQLColAttribute tries to set the FIELD_INFO (using protocol 3). */static BOOL ColAttSet(StatementClass *stmt, TABLE_INFO *rti){	QResultClass	*res = SC_get_Curres(stmt);	IRDFields	*irdflds = SC_get_IRDF(stmt);	COL_INFO	*col_info = NULL;	FIELD_INFO	**fi, *wfi;	OID		reloid = 0;	Int2		attid;	int		i, num_fields;	BOOL		fi_reuse, updatable;mylog("ColAttSet in\n");	if (rti)	{		if (reloid = rti->table_oid, 0 == reloid)			return FALSE;		if (0 != (rti->flags & TI_COLATTRIBUTE))			return TRUE;		col_info = rti->col_info;	}	if (!QR_command_maybe_successful(res))		return FALSE;	if (num_fields = QR_NumPublicResultCols(res), num_fields <= 0)		return FALSE;	fi = irdflds->fi;	if (num_fields > (int) irdflds->allocated)	{		if (!allocateFields(irdflds, num_fields))			return FALSE;		fi = irdflds->fi;	}	setNumFields(irdflds, num_fields);	updatable = rti ? TI_is_updatable(rti) : FALSE;mylog("updatable=%d tab=%d fields=%d", updatable, stmt->ntab, num_fields);	if (updatable)	{		if (1 > stmt->ntab)			updatable = FALSE;		else if (has_multi_table(stmt))			updatable = FALSE;	}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?