It's not necessary to cast any more the 2nd parameter of {call a_b_c_d_e(?, ?, ?, ?, ?)} in odbc-escapes-test.
PutDataClass *pdata;
} PutDataInfo;
+#define PARSE_PARAM_CAST FALSE
+#define EXEC_PARAM_CAST TRUE
+#define SIMPLE_PARAM_CAST TRUE
+
#define CALC_BOOKMARK_ADDR(book, offset, bind_size, index) \
(book->buffer + offset + \
(bind_size > 0 ? bind_size : (SQL_C_VARBOOKMARK == book->returntype ? book->buflen : sizeof(UInt4))) * index)
void SC_param_next(const StatementClass*, int *param_number, ParameterInfoClass **, ParameterImplClass **);
RETCODE prepareParameters(StatementClass *stmt, BOOL fake_params);
-RETCODE prepareParametersNoDesc(StatementClass *stmt, BOOL fake_params);
+RETCODE prepareParametersNoDesc(StatementClass *stmt, BOOL fake_params, BOOL param_cast);
int decideHowToPrepare(StatementClass *stmt, BOOL force);
#endif
#define FLGB_BINARY_AS_POSSIBLE (1L << 9)
#define FLGB_LITERAL_EXTENSION (1L << 10)
#define FLGB_HEX_BIN_FORMAT (1L << 11)
+#define FLGB_PARAM_CAST (1L << 12)
typedef struct _QueryBuild {
char *query_statement;
size_t str_alsize;
* query, in UseServerSidePrepare=0 mode.
*/
RETCODE
-prepareParametersNoDesc(StatementClass *stmt, BOOL fake_params)
+prepareParametersNoDesc(StatementClass *stmt, BOOL fake_params, BOOL param_cast)
{
CSTR func = "process_statements";
RETCODE retval;
if (QB_initialize(qb, qp->stmt_len, stmt,
fake_params ? RPM_FAKE_PARAMS : RPM_BUILDING_PREPARE_STATEMENT) < 0)
return SQL_ERROR;
+ if (param_cast)
+ qb->flags |= FLGB_PARAM_CAST;
for (qp->opos = 0; qp->opos < qp->stmt_len; qp->opos++)
{
inolog("prepareParameters\n");
- if (prepareParametersNoDesc(stmt, fake_params) == SQL_ERROR)
+ if (prepareParametersNoDesc(stmt, fake_params, PARSE_PARAM_CAST) == SQL_ERROR)
return SQL_ERROR;
return desc_params_and_sync(stmt);
}
retval = SQL_ERROR;
goto cleanup;
}
+ if (SIMPLE_PARAM_CAST)
+ qb->flags |= FLGB_PARAM_CAST;
new_statement = qb->query_statement;
/* For selects, prepend a declare cursor to the statement */
}
}
+static BOOL
+parameter_is_with_cast(const QueryParse *qp)
+{
+ const char *str = F_OldPtr(qp);
+ if ('?' != *str) return FALSE;
+ while (isspace(*(++str))) ;
+ if (strncmp(str, "::", 2) == 0)
+ return TRUE;
+ if (strnicmp(str, "as", 2) != 0)
+ return FALSE;
+ if (isspace(str[2]))
+ return TRUE;
+ return FALSE;
+}
/*
* Resolve one parameter.
*
char pnum[16];
qb->dollar_number++;
- sprintf(pnum, "$%d", qb->dollar_number);
+ if (ipara &&
+ SQL_PARAM_OUTPUT != ipara->paramType &&
+ (qb->flags & FLGB_PARAM_CAST) != 0 &&
+ !parameter_is_with_cast(qp))
+ sprintf(pnum, "$%d%s", qb->dollar_number, sqltype_to_pgcast(conn, ipara->SQLType));
+ else
+ sprintf(pnum, "$%d", qb->dollar_number);
CVT_APPEND_STR(qb, pnum);
return SQL_SUCCESS;
}
if (add_parens)
CVT_APPEND_CHAR(qb, ')');
- if (lastadd)
+ if (lastadd && (FLGB_PARAM_CAST & qb->flags) != 0)
CVT_APPEND_STR(qb, lastadd);
}
/*
- * This is used when binding a query parameter, to decide which PostgreSQL
+ * This was used when binding a query parameter, to decide which PostgreSQL
* datatype to send to the server, depending on the SQL datatype that was
* used in the SQLBindParameter call.
*
- * For most types, this returns 0, which means that the server should treat
- * the parameter the same as an untyped literal string, and deduce the type
- * based on the context.
- *
- * This is different from sqltype_to_pgtype(), which doesn't return 0 but
- * tries to give a closer match to the actual datatype.
+ * However it is inflexible and rather harmful.
+ * Now this function always returns 0.
+ * We use cast instead to keep regression test results
+ * in order to keep regression test results.
*/
OID
sqltype_to_bind_pgtype(const ConnectionClass *conn, SQLSMALLINT fSqlType)
{
- OID pgType;
+ OID pgType = 0;
+
+ return pgType;
+}
+
+/*
+ * Casting parameters e.g. ?::timestamp is much more flexible
+ * than specifying parameter datatype oids determined by
+ * sqltype_to_bind_pgtype() via parse message.
+ */
+const char *
+sqltype_to_pgcast(const ConnectionClass *conn, SQLSMALLINT fSqlType)
+{
+ const char *pgCast = NULL_STRING;
switch (fSqlType)
{
case SQL_BINARY:
case SQL_VARBINARY:
- pgType = PG_TYPE_BYTEA;
+ pgCast = "::bytea";
break;
-
- case SQL_LONGVARBINARY:
- /*
- * SQL_LONGVARBINARY could mean a large object, but it could also
- * mean bytea, in particular with ByteaAsLongVarBinary=1. With
- * large binary parameters, there isn't much danger of confusing
- * the datatype in practice - you typically just INSERT them
- * into a table or UPDATE them, and don't apply any operators to
- * them. So instead of trying to guess whether this is a 'bytea'
- * or large object, let the server decide.
- */
- pgType = 0;
- break;
-
case SQL_TYPE_DATE:
case SQL_DATE:
- pgType = PG_TYPE_DATE;
+ pgCast = "::date";
break;
-
case SQL_DECIMAL:
case SQL_NUMERIC:
- pgType = PG_TYPE_NUMERIC;
+ pgCast = "::numeric";
break;
-
case SQL_BIGINT:
- pgType = PG_TYPE_INT8;
+ pgCast = "::int8";
break;
-
case SQL_INTEGER:
- pgType = PG_TYPE_INT4;
+ pgCast = "::int4";
break;
-
case SQL_REAL:
- pgType = PG_TYPE_FLOAT4;
+ pgCast = "::float4";
break;
-
case SQL_SMALLINT:
case SQL_TINYINT:
- pgType = PG_TYPE_INT2;
+ pgCast = "::int2";
break;
-
case SQL_TIME:
case SQL_TYPE_TIME:
- pgType = PG_TYPE_TIME;
+ pgCast = "::time";
break;
-
case SQL_TIMESTAMP:
case SQL_TYPE_TIMESTAMP:
- pgType = PG_TYPE_DATETIME;
+ pgCast = "::timestamp";
break;
-
case SQL_GUID:
if (PG_VERSION_GE(conn, 8.3))
- pgType = PG_TYPE_UUID;
- else
- pgType = 0;
+ pgCast = "::uuid";
break;
-
case SQL_INTERVAL_MONTH:
case SQL_INTERVAL_YEAR:
case SQL_INTERVAL_YEAR_TO_MONTH:
case SQL_INTERVAL_HOUR_TO_MINUTE:
case SQL_INTERVAL_HOUR_TO_SECOND:
case SQL_INTERVAL_MINUTE_TO_SECOND:
- pgType = PG_TYPE_INTERVAL;
- break;
-
- case SQL_CHAR:
-#ifdef UNICODE_SUPPORT
- case SQL_WCHAR:
-#endif /* UNICODE_SUPPORT */
- case SQL_LONGVARCHAR:
-#ifdef UNICODE_SUPPORT
- case SQL_WLONGVARCHAR:
-#endif /* UNICODE_SUPPORT */
- case SQL_VARCHAR:
-#if UNICODE_SUPPORT
- case SQL_WVARCHAR:
-#endif /* UNICODE_SUPPORT */
- default:
- /* The default is to let the server choose */
- pgType = 0;
+ pgCast = "::interval";
break;
}
- return pgType;
+ return pgCast;
}
OID
OID pg_true_type(const ConnectionClass *, OID, OID);
OID sqltype_to_pgtype(const ConnectionClass *conn, SQLSMALLINT fSqlType);
OID sqltype_to_bind_pgtype(const ConnectionClass *conn, SQLSMALLINT fSqlType);
+const char *sqltype_to_pgcast(const ConnectionClass *conn, SQLSMALLINT fSqlType);
SQLSMALLINT pgtype_to_concise_type(const StatementClass *stmt, OID type, int col);
SQLSMALLINT pgtype_to_sqldesctype(const StatementClass *stmt, OID type, int col);
-/*-------
+/*------
* Module: statement.c
*
* Description: This module contains functions related to creating
if (!stmt->processed_statements)
{
- if (prepareParametersNoDesc(stmt, FALSE) == SQL_ERROR)
+ if (prepareParametersNoDesc(stmt, FALSE, EXEC_PARAM_CAST) == SQL_ERROR)
goto cleanup;
}
pstmt = stmt->processed_statements;
+ mylog("%s execParams query=%s nParams=%d\n", __FUNCTION__, pstmt->query, nParams);
pgres = PQexecParams(conn->pqconn,
pstmt->query,
nParams,
}
/* prepareParameters() set plan name, so don't fetch this earlier */
- plan_name = stmt->plan_name ? stmt->plan_name : "";
+ plan_name = stmt->plan_name ? stmt->plan_name : NULL_STRING;
/* already prepared */
+ mylog("%s execPrepared plan=%s nParams=%d\n", __FUNCTION__, plan_name, nParams);
pgres = PQexecPrepared(conn->pqconn,
plan_name, /* portal name == plan name */
nParams,
Result set:
2014-12-22 21:31:41
-Query: {call a_b_c_d_e(?, ?::timestamp, ?, ?, ?)}
+Query: {call a_b_c_d_e(?, ?, ?, ?, ?)}
Param 1 is an OUT parameter
Param 2: 2017-02-23 11:34:46
Param 3 is an I-O parameter
Result set:
2014-12-22 21:31:41
-Query: {call a_b_c_d_e(?, ?::timestamp, ?, ?, ?)}
+Query: {call a_b_c_d_e(?, ?, ?, ?, ?)}
Param 1 is an OUT parameter
Param 2: 2017-02-23 11:34:46
Param 3 is an I-O parameter
executeQuery(hstmt);
/**** call procedure with out and i-o parameters ****/
- prepareQuery(hstmt, "{call a_b_c_d_e(?, ?::timestamp, ?, ?, ?)}");
+ prepareQuery(hstmt, "{call a_b_c_d_e(?, ?, ?, ?, ?)}");
memset(outbuf1, 0, sizeof(outbuf1));
bindOutParamString(hstmt, 1, outbuf1, sizeof(outbuf1) - 1, 0);
bindParamString(hstmt, 2, "2017-02-23 11:34:46");