static int inquireHowToPrepare(const StatementClass *stmt) { ConnectionClass *conn; ConnInfo *ci; int ret = 0; conn = SC_get_conn(stmt); ci = &(conn->connInfo); if (!ci->use_server_side_prepare || PG_VERSION_LT(conn, 7.3)) { /* Do prepare operations by the driver itself */ return PREPARE_BY_THE_DRIVER; } if (NOT_YET_PREPARED == stmt->prepared) { SQLSMALLINT num_params; if (STMT_TYPE_DECLARE == stmt->statement_type && PG_VERSION_LT(conn, 8.0)) { return PREPARE_BY_THE_DRIVER; } if (stmt->multi_statement < 0) PGAPI_NumParams((StatementClass *) stmt, &num_params); if (stmt->multi_statement > 0) /* would divide the query into multiple commands and apply V3 parse requests for each of them */ ret = PROTOCOL_74(ci) ? PARSE_REQ_FOR_INFO : PREPARE_BY_THE_DRIVER; else if (PROTOCOL_74(ci)) { if (SC_may_use_cursor(stmt)) { if (ci->drivers.use_declarefetch) return PARSE_REQ_FOR_INFO; else if (SQL_CURSOR_FORWARD_ONLY != stmt->options.cursor_type) ret = PARSE_REQ_FOR_INFO; else ret = PARSE_TO_EXEC_ONCE; } else ret = PARSE_TO_EXEC_ONCE; } else { if (SC_may_use_cursor(stmt) && (SQL_CURSOR_FORWARD_ONLY != stmt->options.cursor_type || ci->drivers.use_declarefetch)) ret = PREPARE_BY_THE_DRIVER; else if (SC_is_prepare_statement(stmt)) ret = USING_PREPARE_COMMAND; else ret = PREPARE_BY_THE_DRIVER; } } if (SC_is_prepare_statement(stmt) && (PARSE_TO_EXEC_ONCE == ret)) ret = NAMED_PARSE_REQUEST; return ret; }
RETCODE SQL_API SQLNumParams(HSTMT hstmt, SQLSMALLINT *pcpar) { RETCODE ret; StatementClass *stmt = (StatementClass *) hstmt; mylog("[SQLNumParams]"); ENTER_STMT_CS(stmt); SC_clear_error(stmt); StartRollbackState(stmt); ret = PGAPI_NumParams(hstmt, pcpar); ret = DiscardStatementSvp(stmt, ret, FALSE); LEAVE_STMT_CS(stmt); return ret; }
/* Execute a prepared SQL statement */ RETCODE SQL_API PGAPI_Execute(HSTMT hstmt, UWORD flag) { CSTR func = "PGAPI_Execute"; StatementClass *stmt = (StatementClass *) hstmt; RETCODE retval = SQL_SUCCESS; ConnectionClass *conn; APDFields *apdopts; IPDFields *ipdopts; SQLLEN i, start_row, end_row; BOOL exec_end, recycled = FALSE, recycle = TRUE; SQLSMALLINT num_params; mylog("%s: entering...%x\n", func, flag); if (!stmt) { SC_log_error(func, "", NULL); mylog("%s: NULL statement so return SQL_INVALID_HANDLE\n", func); return SQL_INVALID_HANDLE; } conn = SC_get_conn(stmt); apdopts = SC_get_APDF(stmt); /* * If the statement is premature, it means we already executed it from * an SQLPrepare/SQLDescribeCol type of scenario. So just return * success. */ if (stmt->prepare && stmt->status == STMT_PREMATURE) { if (stmt->inaccurate_result) { stmt->exec_current_row = -1; SC_recycle_statement(stmt); } else { stmt->status = STMT_FINISHED; if (NULL == SC_get_errormsg(stmt)) { mylog("%s: premature statement but return SQL_SUCCESS\n", func); retval = SQL_SUCCESS; goto cleanup; } else { SC_set_error(stmt,STMT_INTERNAL_ERROR, "premature statement so return SQL_ERROR", func); retval = SQL_ERROR; goto cleanup; } } } mylog("%s: clear errors...\n", func); SC_clear_error(stmt); if (!stmt->statement) { SC_set_error(stmt, STMT_NO_STMTSTRING, "This handle does not have a SQL statement stored in it", func); mylog("%s: problem with handle\n", func); return SQL_ERROR; } #define return DONT_CALL_RETURN_FROM_HERE??? if (stmt->exec_current_row > 0) { /* * executing an array of parameters. * Don't recycle the statement. */ recycle = FALSE; } else if (PREPARED_PERMANENTLY == stmt->prepared || PREPARED_TEMPORARILY == stmt->prepared) { QResultClass *res; /* * re-executing an prepared statement. * Don't recycle the statement but * discard the old result. */ recycle = FALSE; if (res = SC_get_Result(stmt), res) QR_close_result(res, FALSE); } /* * If SQLExecute is being called again, recycle the statement. Note * this should have been done by the application in a call to * SQLFreeStmt(SQL_CLOSE) or SQLCancel. */ else if (stmt->status == STMT_FINISHED) { mylog("%s: recycling statement (should have been done by app)...\n", func); /******** Is this really NEEDED ? ******/ SC_recycle_statement(stmt); recycled = TRUE; } /* Check if the statement is in the correct state */ else if ((stmt->prepare && stmt->status != STMT_READY) || (stmt->status != STMT_ALLOCATED && stmt->status != STMT_READY)) { SC_set_error(stmt, STMT_STATUS_ERROR, "The handle does not point to a statement that is ready to be executed", func); mylog("%s: problem with statement\n", func); retval = SQL_ERROR; goto cleanup; } if (start_row = stmt->exec_start_row, start_row < 0) start_row = 0; if (end_row = stmt->exec_end_row, end_row < 0) end_row = (SQLINTEGER) apdopts->paramset_size - 1; if (stmt->exec_current_row < 0) stmt->exec_current_row = start_row; ipdopts = SC_get_IPDF(stmt); num_params = stmt->num_params; if (num_params < 0) PGAPI_NumParams(stmt, &num_params); if (stmt->exec_current_row == start_row) { /* We sometimes need to know about the PG type of binding parameters even in case of non-prepared statements. */ int nCallParse = doNothing; if (NOT_YET_PREPARED == stmt->prepared) { switch (nCallParse = HowToPrepareBeforeExec(stmt, TRUE)) { case shouldParse: if (retval = prepareParameters(stmt, TRUE), SQL_ERROR == retval) goto cleanup; break; } } mylog("prepareParameters was %s called, prepare state:%d\n", shouldParse == nCallParse ? "" : "not", stmt->prepare); if (ipdopts->param_processed_ptr) *ipdopts->param_processed_ptr = 0; #if (ODBCVER >= 0x0300) /* * Initialize the param_status_ptr */ if (ipdopts->param_status_ptr) { for (i = 0; i <= end_row; i++) ipdopts->param_status_ptr[i] = SQL_PARAM_UNUSED; } #endif /* ODBCVER */ if (recycle && !recycled) SC_recycle_statement(stmt); if (isSqlServr() && !stmt->internal && 0 != stmt->prepare && PG_VERSION_LT(conn, 8.4) && SC_can_parse_statement(stmt)) parse_sqlsvr(stmt); } /* * Clear any old result sets before executing. The prepare stage might've * created one. */ SC_set_Result(stmt, NULL); next_param_row: #if (ODBCVER >= 0x0300) if (apdopts->param_operation_ptr) { while (apdopts->param_operation_ptr[stmt->exec_current_row] == SQL_PARAM_IGNORE) { if (stmt->exec_current_row >= end_row) { stmt->exec_current_row = -1; retval = SQL_SUCCESS; goto cleanup; } ++stmt->exec_current_row; } } /* * Initialize the current row status */ if (ipdopts->param_status_ptr) ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; #endif /* ODBCVER */ /* * Check if statement has any data-at-execute parameters when it is * not in SC_pre_execute. */ if (!stmt->pre_executing) { /* * The bound parameters could have possibly changed since the last * execute of this statement? Therefore check for params and * re-copy. */ SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0; SQLINTEGER bind_size = apdopts->param_bind_type; SQLLEN current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; Int4 num_p = num_params < apdopts->allocated ? num_params : apdopts->allocated; /* * Increment the number of currently processed rows */ if (ipdopts->param_processed_ptr) (*ipdopts->param_processed_ptr)++; stmt->data_at_exec = -1; for (i = 0; i < num_p; i++) { SQLLEN *pcVal = apdopts->parameters[i].used; apdopts->parameters[i].data_at_exec = FALSE; if (pcVal) { if (bind_size > 0) pcVal = LENADDR_SHIFT(pcVal, offset + bind_size * current_row); else pcVal = LENADDR_SHIFT(pcVal, offset) + current_row; if (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET) apdopts->parameters[i].data_at_exec = TRUE; } /* Check for data at execution parameters */ if (apdopts->parameters[i].data_at_exec) { mylog("The %dth parameter of %d-row is data at exec(%d)\n", i, current_row, *pcVal); if (stmt->data_at_exec < 0) stmt->data_at_exec = 1; else stmt->data_at_exec++; } } /* * If there are some data at execution parameters, return need * data */ /* * SQLParamData and SQLPutData will be used to send params and * execute the statement. */ if (stmt->data_at_exec > 0) { retval = SQL_NEED_DATA; goto cleanup; } } if (0 != (flag & PODBC_WITH_HOLD)) SC_set_with_hold(stmt); retval = Exec_with_parameters_resolved(stmt, &exec_end); if (!exec_end) { stmt->curr_param_result = 0; goto next_param_row; } cleanup: mylog("retval=%d\n", retval); SC_setInsertedTable(stmt, retval); #undef return if (stmt->internal) retval = DiscardStatementSvp(stmt, retval, FALSE); return retval; }
static int HowToPrepareBeforeExec(StatementClass *stmt, BOOL checkOnly) { SQLSMALLINT num_params = stmt->num_params; ConnectionClass *conn = SC_get_conn(stmt); ConnInfo *ci = &(conn->connInfo); int nCallParse = doNothing, how_to_prepare = 0; BOOL bNeedsTrans = FALSE; if (num_params < 0) PGAPI_NumParams(stmt, &num_params); how_to_prepare = decideHowToPrepare(stmt, checkOnly); if (checkOnly) { if (num_params <= 0) return doNothing; } else { switch (how_to_prepare) { case USING_PREPARE_COMMAND: return checkOnly ? doNothing : usingCommand; case NAMED_PARSE_REQUEST: return shouldParse; case PARSE_TO_EXEC_ONCE: switch (stmt->prepared) { case PREPARED_TEMPORARILY: nCallParse = preferParse; break; default: if (num_params <= 0) nCallParse = NOPARAM_ONESHOT_CALL_PARSE; else nCallParse = ONESHOT_CALL_PARSE; } break; default: return doNothing; } } if (PG_VERSION_LE(conn, 7.3) || !PROTOCOL_74(ci)) return nCallParse; if (num_params > 0) { int param_number = -1; ParameterInfoClass *apara; ParameterImplClass *ipara; OID pgtype; while (TRUE) { SC_param_next(stmt, ¶m_number, &apara, &ipara); if (!ipara || !apara) break; pgtype = PIC_get_pgtype(*ipara); if (checkOnly) { switch (ipara->SQLType) { case SQL_LONGVARBINARY: if (0 == pgtype) { if (ci->bytea_as_longvarbinary && 0 != conn->lobj_type) nCallParse = shouldParse; } break; case SQL_CHAR: if (ci->cvt_null_date_string) nCallParse = shouldParse; break; case SQL_VARCHAR: if (ci->drivers.bools_as_char && PG_WIDTH_OF_BOOLS_AS_CHAR == ipara->column_size) nCallParse = shouldParse; break; } } else { BOOL bBytea = FALSE; switch (ipara->SQLType) { case SQL_LONGVARBINARY: if (conn->lobj_type == pgtype || PG_TYPE_OID == pgtype) bNeedsTrans = TRUE; else if (PG_TYPE_BYTEA == pgtype) bBytea = TRUE; else if (0 == pgtype) { if (ci->bytea_as_longvarbinary) bBytea = TRUE; else bNeedsTrans = TRUE; } if (bBytea) if (nCallParse < preferParse) nCallParse = preferParse; break; } } } } if (bNeedsTrans && PARSE_TO_EXEC_ONCE == how_to_prepare) { if (!CC_is_in_trans(conn) && CC_does_autocommit(conn)) nCallParse = doNothing; } return nCallParse; }
/* * Supplies parameter data at execution time. * Used in conjuction with SQLPutData. */ RETCODE SQL_API PGAPI_ParamData( HSTMT hstmt, PTR FAR * prgbValue) { CSTR func = "PGAPI_ParamData"; StatementClass *stmt = (StatementClass *) hstmt, *estmt; APDFields *apdopts; IPDFields *ipdopts; RETCODE retval; int i; Int2 num_p; ConnectionClass *conn = NULL; mylog("%s: entering...\n", func); if (!stmt) { SC_log_error(func, "", NULL); retval = SQL_INVALID_HANDLE; goto cleanup; } conn = SC_get_conn(stmt); estmt = stmt->execute_delegate ? stmt->execute_delegate : stmt; apdopts = SC_get_APDF(estmt); mylog("%s: data_at_exec=%d, params_alloc=%d\n", func, estmt->data_at_exec, apdopts->allocated); #define return DONT_CALL_RETURN_FROM_HERE??? if (SC_AcceptedCancelRequest(stmt)) { SC_set_error(stmt, STMT_OPERATION_CANCELLED, "Cancel the statement, sorry", func); retval = SQL_ERROR; goto cleanup; } if (estmt->data_at_exec < 0) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "No execution-time parameters for this statement", func); retval = SQL_ERROR; goto cleanup; } if (estmt->data_at_exec > apdopts->allocated) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Too many execution-time parameters were present", func); retval = SQL_ERROR; goto cleanup; } /* close the large object */ if (estmt->lobj_fd >= 0) { odbc_lo_close(conn, estmt->lobj_fd); /* commit transaction if needed */ if (!CC_cursor_count(conn) && CC_does_autocommit(conn)) { if (!CC_commit(conn)) { SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction", func); retval = SQL_ERROR; goto cleanup; } } estmt->lobj_fd = -1; } /* Done, now copy the params and then execute the statement */ ipdopts = SC_get_IPDF(estmt); inolog("ipdopts=%p\n", ipdopts); if (estmt->data_at_exec == 0) { BOOL exec_end; UWORD flag = SC_is_with_hold(stmt) ? PODBC_WITH_HOLD : 0; retval = Exec_with_parameters_resolved(estmt, &exec_end); if (exec_end) { /**SC_reset_delegate(retval, stmt);**/ retval = dequeueNeedDataCallback(retval, stmt); goto cleanup; } if (retval = PGAPI_Execute(estmt, flag), SQL_NEED_DATA != retval) { goto cleanup; } } /* * Set beginning param; if first time SQLParamData is called , start * at 0. Otherwise, start at the last parameter + 1. */ i = estmt->current_exec_param >= 0 ? estmt->current_exec_param + 1 : 0; num_p = estmt->num_params; if (num_p < 0) PGAPI_NumParams(estmt, &num_p); inolog("i=%d allocated=%d num_p=%d\n", i, apdopts->allocated, num_p); if (num_p > apdopts->allocated) num_p = apdopts->allocated; /* At least 1 data at execution parameter, so Fill in the token value */ for (; i < num_p; i++) { inolog("i=%d", i); if (apdopts->parameters[i].data_at_exec) { inolog(" at exec buffer=%p", apdopts->parameters[i].buffer); estmt->data_at_exec--; estmt->current_exec_param = i; estmt->put_data = FALSE; if (prgbValue) { /* returns token here */ if (stmt->execute_delegate) { SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0; SQLLEN perrow = apdopts->param_bind_type > 0 ? apdopts->param_bind_type : apdopts->parameters[i].buflen; inolog(" offset=%d perrow=%d", offset, perrow); *prgbValue = apdopts->parameters[i].buffer + offset + estmt->exec_current_row * perrow; } else *prgbValue = apdopts->parameters[i].buffer; } break; } inolog("\n"); } retval = SQL_NEED_DATA; inolog("return SQL_NEED_DATA\n"); cleanup: #undef return SC_setInsertedTable(stmt, retval); if (stmt->internal) retval = DiscardStatementSvp(stmt, retval, FALSE); mylog("%s: returning %d\n", func, retval); return retval; }
/* * Returns the description of a parameter marker. * This function is listed as not being supported by SQLGetFunctions() because it is * used to describe "parameter markers" (not bound parameters), in which case, * the dbms should return info on the markers. Since Postgres doesn't support that, * it is best to say this function is not supported and let the application assume a * data type (most likely varchar). */ RETCODE SQL_API PGAPI_DescribeParam(HSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT FAR * pfSqlType, SQLULEN FAR * pcbParamDef, SQLSMALLINT FAR * pibScale, SQLSMALLINT FAR * pfNullable) { StatementClass *stmt = (StatementClass *) hstmt; CSTR func = "PGAPI_DescribeParam"; IPDFields *ipdopts; RETCODE ret = SQL_SUCCESS; int num_params; OID pgtype; mylog("%s: entering...%d\n", func, ipar); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } SC_clear_error(stmt); ipdopts = SC_get_IPDF(stmt); /*if ((ipar < 1) || (ipar > ipdopts->allocated))*/ num_params = stmt->num_params; if (num_params < 0) { SQLSMALLINT num_p; PGAPI_NumParams(stmt, &num_p); num_params = num_p; } if ((ipar < 1) || (ipar > num_params)) { inolog("num_params=%d\n", stmt->num_params); SC_set_error(stmt, STMT_BAD_PARAMETER_NUMBER_ERROR, "Invalid parameter number for PGAPI_DescribeParam.", func); return SQL_ERROR; } extend_iparameter_bindings(ipdopts, stmt->num_params); #define return DONT_CALL_RETURN_FROM_HERE??? /* StartRollbackState(stmt); */ if (NOT_YET_PREPARED == stmt->prepared) { decideHowToPrepare(stmt, FALSE); inolog("howTo=%d\n", SC_get_prepare_method(stmt)); switch (SC_get_prepare_method(stmt)) { case NAMED_PARSE_REQUEST: case PARSE_TO_EXEC_ONCE: case PARSE_REQ_FOR_INFO: if (ret = prepareParameters(stmt), SQL_ERROR == ret) goto cleanup; } } ipar--; pgtype = PIC_get_pgtype(ipdopts->parameters[ipar]); /* * This implementation is not very good, since it is supposed to * describe */ /* parameter markers, not bound parameters. */ if (pfSqlType) { inolog("[%d].SQLType=%d .PGType=%d\n", ipar, ipdopts->parameters[ipar].SQLType, pgtype); if (ipdopts->parameters[ipar].SQLType) *pfSqlType = ipdopts->parameters[ipar].SQLType; else if (pgtype) *pfSqlType = pgtype_to_concise_type(stmt, pgtype, PG_STATIC); else { ret = SQL_ERROR; SC_set_error(stmt, STMT_EXEC_ERROR, "Unfortunatley couldn't get this paramater's info", func); goto cleanup; } } if (pcbParamDef) { *pcbParamDef = 0; if (ipdopts->parameters[ipar].SQLType) *pcbParamDef = ipdopts->parameters[ipar].column_size; if (0 == *pcbParamDef && pgtype) *pcbParamDef = pgtype_column_size(stmt, pgtype, PG_STATIC, PG_STATIC); } if (pibScale) { *pibScale = 0; if (ipdopts->parameters[ipar].SQLType) *pibScale = ipdopts->parameters[ipar].decimal_digits; else if (pgtype) *pibScale = pgtype_scale(stmt, pgtype, -1); } if (pfNullable) *pfNullable = pgtype_nullable(SC_get_conn(stmt), ipdopts->parameters[ipar].paramType); cleanup: #undef return if (stmt->internal) ret = DiscardStatementSvp(stmt, ret, FALSE); return ret; }