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 + -
显示快捷键?