postgres.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,551 行 · 第 1/5 页
C
2,551 行
* Switch to appropriate context for constructing parsetrees. * * We have two strategies depending on whether the prepared statement is * named or not. For a named prepared statement, we do parsing in * MessageContext and copy the finished trees into the prepared * statement's private context; then the reset of MessageContext * releases temporary space used by parsing and planning. For an * unnamed prepared statement, we assume the statement isn't going to * hang around long, so getting rid of temp space quickly is probably * not worth the costs of copying parse/plan trees. So in this case, * we set up a special context for the unnamed statement, and do all * the parsing/planning therein. */ is_named = (stmt_name[0] != '\0'); if (is_named) { /* Named prepared statement --- parse in MessageContext */ oldcontext = MemoryContextSwitchTo(MessageContext); } else { /* Unnamed prepared statement --- release any prior unnamed stmt */ unnamed_stmt_pstmt = NULL; if (unnamed_stmt_context) { DropDependentPortals(unnamed_stmt_context); MemoryContextDelete(unnamed_stmt_context); } unnamed_stmt_context = NULL; /* create context for parsing/planning */ unnamed_stmt_context = AllocSetContextCreate(TopMemoryContext, "unnamed prepared statement", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcontext = MemoryContextSwitchTo(unnamed_stmt_context); } QueryContext = CurrentMemoryContext; /* * Do basic parsing of the query or queries (this should be safe even * if we are in aborted transaction state!) */ parsetree_list = pg_parse_query(query_string); /* * We only allow a single user statement in a prepared statement. This * is mainly to keep the protocol simple --- otherwise we'd need to * worry about multiple result tupdescs and things like that. */ if (length(parsetree_list) > 1) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot insert multiple commands into a prepared statement"))); if (parsetree_list != NIL) { Node *parsetree = (Node *) lfirst(parsetree_list); int i; /* * Get the command name for possible use in status display. */ commandTag = CreateCommandTag(parsetree); /* * If we are in an aborted transaction, reject all commands except * COMMIT/ROLLBACK. It is important that this test occur before * we try to do parse analysis, rewrite, or planning, since all * those phases try to do database accesses, which may fail in * abort state. (It might be safe to allow some additional utility * commands in this state, but not many...) */ if (IsAbortedTransactionBlockState()) { bool allowit = false; if (IsA(parsetree, TransactionStmt)) { TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || stmt->kind == TRANS_STMT_ROLLBACK) allowit = true; } if (!allowit) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block"))); } /* * OK to analyze, rewrite, and plan this query. Note that the * originally specified parameter set is not required to be * complete, so we have to use parse_analyze_varparams(). */ if (log_parser_stats) ResetUsage(); querytree_list = parse_analyze_varparams(parsetree, ¶mTypes, &numParams); /* * Check all parameter types got determined, and convert array * representation to a list for storage. */ param_list = NIL; for (i = 0; i < numParams; i++) { Oid ptype = paramTypes[i]; if (ptype == InvalidOid || ptype == UNKNOWNOID) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_DATATYPE), errmsg("could not determine data type of parameter $%d", i + 1))); param_list = lappendo(param_list, ptype); } if (log_parser_stats) ShowUsage("PARSE ANALYSIS STATISTICS"); querytree_list = pg_rewrite_queries(querytree_list); plantree_list = pg_plan_queries(querytree_list, true); } else { /* Empty input string. This is legal. */ commandTag = NULL; querytree_list = NIL; plantree_list = NIL; param_list = NIL; } /* If we got a cancel signal in analysis or planning, quit */ CHECK_FOR_INTERRUPTS(); /* * Store the query as a prepared statement. See above comments. */ if (is_named) { StorePreparedStatement(stmt_name, query_string, commandTag, querytree_list, plantree_list, param_list); } else { PreparedStatement *pstmt; pstmt = (PreparedStatement *) palloc0(sizeof(PreparedStatement)); /* query_string needs to be copied into unnamed_stmt_context */ pstmt->query_string = pstrdup(query_string); /* the rest is there already */ pstmt->commandTag = commandTag; pstmt->query_list = querytree_list; pstmt->plan_list = plantree_list; pstmt->argtype_list = param_list; pstmt->context = unnamed_stmt_context; /* Now the unnamed statement is complete and valid */ unnamed_stmt_pstmt = pstmt; } MemoryContextSwitchTo(oldcontext); QueryContext = NULL; /* * We do NOT close the open transaction command here; that only * happens when the client sends Sync. Instead, do * CommandCounterIncrement just in case something happened during * parse/plan. */ CommandCounterIncrement(); /* * Send ParseComplete. */ if (whereToSendOutput == Remote) pq_putemptymessage('1'); if (save_log_statement_stats) ShowUsage("PARSE MESSAGE STATISTICS"); debug_query_string = NULL;}/* * exec_bind_message * * Process a "Bind" message to create a portal from a prepared statement */static voidexec_bind_message(StringInfo input_message){ const char *portal_name; const char *stmt_name; int numPFormats; int16 *pformats = NULL; int numParams; int numRFormats; int16 *rformats = NULL; int i; PreparedStatement *pstmt; Portal portal; ParamListInfo params; pgstat_report_activity("<BIND>"); set_ps_display("BIND"); /* * Start up a transaction command so we can call functions etc. (Note * that this will normally change current memory context.) Nothing * happens if we are already in one. */ start_xact_command(); /* Switch back to message context */ MemoryContextSwitchTo(MessageContext); /* Get the fixed part of the message */ portal_name = pq_getmsgstring(input_message); stmt_name = pq_getmsgstring(input_message); /* Get the parameter format codes */ numPFormats = pq_getmsgint(input_message, 2); if (numPFormats > 0) { pformats = (int16 *) palloc(numPFormats * sizeof(int16)); for (i = 0; i < numPFormats; i++) pformats[i] = pq_getmsgint(input_message, 2); } /* Get the parameter value count */ numParams = pq_getmsgint(input_message, 2); if (numPFormats > 1 && numPFormats != numParams) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message has %d parameter formats but %d parameters", numPFormats, numParams))); /* Find prepared statement */ if (stmt_name[0] != '\0') pstmt = FetchPreparedStatement(stmt_name, true); else { /* special-case the unnamed statement */ pstmt = unnamed_stmt_pstmt; if (!pstmt) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("unnamed prepared statement does not exist"))); } if (numParams != length(pstmt->argtype_list)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d", numParams, stmt_name, length(pstmt->argtype_list)))); /* * Create the portal. Allow silent replacement of an existing portal * only if the unnamed portal is specified. */ if (portal_name[0] == '\0') portal = CreatePortal(portal_name, true, true); else portal = CreatePortal(portal_name, false, false); PortalDefineQuery(portal, pstmt->query_string, pstmt->commandTag, pstmt->query_list, pstmt->plan_list, pstmt->context); /* * Fetch parameters, if any, and store in the portal's memory context. * * In an aborted transaction, we can't risk calling user-defined * functions, but we can't fail to Bind either, so bind all parameters * to null values. */ if (numParams > 0) { bool isaborted = IsAbortedTransactionBlockState(); List *l; MemoryContext oldContext; oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); params = (ParamListInfo) palloc0((numParams + 1) * sizeof(ParamListInfoData)); i = 0; foreach(l, pstmt->argtype_list) { Oid ptype = lfirsto(l); int32 plength; bool isNull; plength = pq_getmsgint(input_message, 4); isNull = (plength == -1); if (!isNull) { const char *pvalue = pq_getmsgbytes(input_message, plength); if (isaborted) { /* We don't bother to check the format in this case */ isNull = true; } else { int16 pformat; StringInfoData pbuf; char csave; if (numPFormats > 1) pformat = pformats[i]; else if (numPFormats > 0) pformat = pformats[0]; else pformat = 0; /* default = text */ /* * Rather than copying data around, we just set up a * phony StringInfo pointing to the correct portion of * the message buffer. We assume we can scribble on * the message buffer so as to maintain the convention * that StringInfos have a trailing null. This is * grotty but is a big win when dealing with very * large parameter strings. */ pbuf.data = (char *) pvalue; pbuf.maxlen = plength + 1; pbuf.len = plength; pbuf.cursor = 0; csave = pbuf.data[plength]; pbuf.data[plength] = '\0'; if (pformat == 0) { Oid typInput; Oid typElem; char *pstring; getTypeInputInfo(ptype, &typInput, &typElem); /* * We have to do encoding conversion before * calling the typinput routine. */ pstring = (char *) pg_client_to_server((unsigned char *) pbuf.data, plength); params[i].value = OidFunctionCall3(typInput, CStringGetDatum(pstring), ObjectIdGetDatum(typElem), Int32GetDatum(-1)); /* Free result of encoding conversion, if any */ if (pstring != pbuf.data) pfree(pstring); } else if (pformat == 1) { Oid typReceive; Oid typElem; /* * Call the parameter type's binary input * converter */ getTypeBinaryInputInfo(ptype, &typReceive, &typElem); params[i].value = OidFunctionCall2(typReceive, PointerGetDatum(&pbuf), ObjectIdGetDatum(typElem)); /* Trouble if it didn't eat the whole buffer */ if (pbuf.cursor != pbuf.len) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format in bind parameter %d", i + 1))); } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat))); } /* Restore message buffer contents */ pbuf.data[plength] = csave; } } params[i].kind = PARAM_NUM; params[i].id = i + 1; params[i].isnull = isNull; i++; } params[i].kind = PARAM_INVALID; MemoryContextSwitchTo(oldContext); } else params = NULL; /* Get the result format codes */ numRFormats = pq_getmsgint(input_message, 2); if (numRFormats > 0) { rformats = (int16 *) palloc(numRFormats * sizeof(int16)); for (i = 0; i < numRFormats; i++) rformats[i] = pq_getmsgint(input_message, 2); } pq_getmsgend(input_message); /* * Start portal execution. */ PortalStart(portal, params); /* * Apply the result format requests to the portal. */ PortalSetResultFormat(portal, numRFormats, rformats); /* * Send BindComplete. */ if (whereToSendOutput == Remote) pq_putemptymessage('2');}/* * exec_execute_message * * Process an "Execute" message for a portal */static voidexec_execute_message(const char *portal_name, long max_rows){ CommandDest dest; DestReceiver *receiver; Portal portal; bool is_trans_stmt = false; bool is_trans_exit = false; bool completed; char completionTag[COMPLETION_TAG_BUFSIZE]; /* Adjust destination to tell printtup.c what to do */ dest = whereToSendOutput; if (dest == Remote) dest = RemoteExecute; portal = GetPortalByName(portal_name); if (!PortalIsValid(portal)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR), errmsg("portal \"%s\" does not exist", portal_name))); /* * If the original query was a null string, just return * EmptyQueryResponse. */ if (portal->commandTag == NULL) { Assert(portal->parseTrees == NIL); NullCommand(dest); return; } if (portal->sourceText) { debug_query_string = portal->sourceText; pgstat_report_activity(portal->sourceText); } else { debug_query_string = "execute message"; pgstat_report_activity("<EXECUTE>"); } set_ps_display(portal->commandTag); BeginCommand(portal->commandTag, dest); /* Check for transaction-control commands */ if (length(portal->parseTrees) == 1)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?