SQLRETURN EsSQLSetStmtAttrW()

in driver/handles.c [589:918]


SQLRETURN EsSQLSetStmtAttrW(
	SQLHSTMT           StatementHandle,
	SQLINTEGER         Attribute,
	SQLPOINTER         ValuePtr,
	SQLINTEGER         BufferLength)
{
	SQLRETURN ret;
	SQLULEN ulen;
	esodbc_desc_st *desc;
	esodbc_stmt_st *stmt = STMH(StatementHandle);

	/*INDENT-OFF*/
	switch(Attribute) {
		case SQL_ATTR_USE_BOOKMARKS:
			DBGH(stmt, "setting use-bookmarks to: %llu.", (uint64_t)ValuePtr);
			if ((SQLULEN)ValuePtr != SQL_UB_OFF) {
				ERRH(stmt, "bookmarks are not supported by driver.");
				RET_HDIAG(stmt, SQL_STATE_HYC00,
						"bookmarks are not supported by driver", 0);
			}
			break;

		do {
		/* "If this field is non-null, the driver dereferences the pointer,
		 * adds the dereferenced value to each of the deferred fields in the
		 * descriptor record (SQL_DESC_DATA_PTR, SQL_DESC_INDICATOR_PTR, and
		 * SQL_DESC_OCTET_LENGTH_PTR), and uses the new pointer values when
		 * binding. It is set to null by default." */
		case SQL_ATTR_ROW_BIND_OFFSET_PTR:
			/* offset in bytes */
			/* "Setting this statement attribute sets the
			 * SQL_DESC_BIND_OFFSET_PTR field in the ARD header." */
			DBGH(stmt, "setting row-bind-offset pointer to: 0x%p.", ValuePtr);
			desc = stmt->ard;
			break;
		case SQL_ATTR_PARAM_BIND_OFFSET_PTR:
			DBGH(stmt, "setting param-bind-offset pointer to: 0x%p.", ValuePtr);
			desc = stmt->apd;
			break;
		} while (0);
			ret = EsSQLSetDescFieldW(desc, NO_REC_NR, SQL_DESC_BIND_OFFSET_PTR,
					ValuePtr, BufferLength);
			if (ret != SQL_SUCCESS) { /* _WITH_INFO wud be "error" here */
				/* if SetDescField() fails, DM will check statement's diag */
				HDIAG_COPY(desc, stmt);
			}
			return ret;

		do {
		/* "Setting this statement attribute sets the SQL_DESC_ARRAY_SIZE
		 * field in the ARD header." */
		case SQL_ATTR_ROW_ARRAY_SIZE:
			DBGH(stmt, "setting row array size to: %d.", (SQLULEN)ValuePtr);
			desc = stmt->ard;
			break;
		/* "Setting this statement attribute sets the SQL_DESC_ARRAY_SIZE
		 * field in the APD header." */
		case SQL_ATTR_PARAMSET_SIZE:
			DBGH(stmt, "setting param set size to: %d.", (SQLULEN)ValuePtr);
			desc = stmt->apd;
			break;
		} while (0);
			ret = EsSQLSetDescFieldW(desc, NO_REC_NR, SQL_DESC_ARRAY_SIZE,
					ValuePtr, BufferLength);
			if (ret != SQL_SUCCESS) { /* _WITH_INFO wud be "error" here */
				/* if SetDescField() fails, DM will check statement's diag */
				HDIAG_COPY(desc, stmt);
			}
			return ret;

		do {
		/* "sets the binding orientation to be used when SQLFetch or
		 * SQLFetchScroll is called on the associated statement" */
		/* "Setting this statement attribute sets the SQL_DESC_BIND_TYPE field
		 * in the ARD header." */
		case SQL_ATTR_ROW_BIND_TYPE:
			DBGH(stmt, "setting row bind type to: %d.", (SQLULEN)ValuePtr);
			/* value is SQL_BIND_BY_COLUMN (0UL) or struct len  */
			/* "the driver can calculate the address of the data for a
			 * particular row and column as:
			 * Address = Bound Address + ((Row Number - 1) * Structure Size)"
			 * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/column-wise-binding
			 * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/row-wise-binding
			 */
			desc = stmt->ard;
			break;
		case SQL_ATTR_PARAM_BIND_TYPE:
			DBGH(stmt, "setting param bind type to: %d.", (SQLULEN)ValuePtr);
			desc = stmt->apd;
			break;
		} while (0);
			ret = EsSQLSetDescFieldW(desc, NO_REC_NR, SQL_DESC_BIND_TYPE,
					/* note: SetStmt()'s spec defineds the ValuePtr as
					 * SQLULEN, but SetDescField()'s as SQLUINTEGER.. */
					ValuePtr, BufferLength);
			if (ret != SQL_SUCCESS) { /* _WITH_INFO wud be "error" here */
				/* if SetDescField() fails, DM will check statement's diag */
				HDIAG_COPY(desc, stmt);
			}
			return ret;

		do {
		/* "an array of SQLUSMALLINT values containing row status values after
		 * a call to SQLFetch or SQLFetchScroll." */
		/* https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/row-status-array */
		/* "Setting this statement attribute sets the
		 * SQL_DESC_ARRAY_STATUS_PTR field in the IRD header." */
		case SQL_ATTR_ROW_STATUS_PTR:
			// TODO: call SQLSetDescField(IRD) here?
			DBGH(stmt, "setting row status pointer to: 0x%p.", ValuePtr);
			desc = stmt->ird;
			break;
		case SQL_ATTR_PARAM_STATUS_PTR:
			DBGH(stmt, "setting param status pointer to: 0x%p.", ValuePtr);
			desc = stmt->ipd;
			break;
		case SQL_ATTR_ROW_OPERATION_PTR:
			DBGH(stmt, "setting row operation array pointer to: 0x%p.",
					ValuePtr);
			desc = stmt->ard;
			break;
		case SQL_ATTR_PARAM_OPERATION_PTR:
			DBGH(stmt, "setting param operation array pointer to: 0x%p.",
					ValuePtr);
			desc = stmt->apd;
			break;
		} while (0);
			ret = EsSQLSetDescFieldW(desc, NO_REC_NR,
					SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, BufferLength);
			if (ret != SQL_SUCCESS) { /* _WITH_INFO wud be "error" here */
				/* if SetDescField() fails, DM will check statement's diag */
				HDIAG_COPY(desc, stmt);
			}
			return ret;

		do {
		/* "Setting this statement attribute sets the
		 * SQL_DESC_ROWS_PROCESSED_PTR field in the IRD header." */
		case SQL_ATTR_ROWS_FETCHED_PTR:
			DBGH(stmt, "setting rows fetched pointer to: 0x%p.", ValuePtr);
			/* NOTE: documentation writes about "ARD", while also stating that
			 * this field is unused in the ARD. I assume the former as wrong */
			desc = stmt->ird;
			break;
		case SQL_ATTR_PARAMS_PROCESSED_PTR:
			DBGH(stmt, "setting params processed pointer to: 0x%p.", ValuePtr);
			desc = stmt->ipd;
			break;
		} while (0);
			ret = EsSQLSetDescFieldW(desc, NO_REC_NR,
					SQL_DESC_ROWS_PROCESSED_PTR, ValuePtr, BufferLength);
			if (ret != SQL_SUCCESS) { /* _WITH_INFO wud be "error" here */
				/* if SetDescField() fails, DM will check statement's diag */
				HDIAG_COPY(desc, stmt);
			}
			return ret;

		case SQL_ATTR_APP_ROW_DESC:
			desc = DSCH(ValuePtr);
			if (desc == stmt->ard) {
				WARNH(stmt, "trying to overwrite ARD with same value (@0x%p).",
						ValuePtr);
				break; /* nop */
			}
			if (desc == &stmt->i_ard || desc == SQL_NULL_HDESC) {
				DBGH(stmt, "unbinding current ARD (@0x%p), rebinding with "
						"implicit value (@0x%p).", stmt->ard, &stmt->i_ard);
				/* re-anonymize the descriptor, makingit re-usable */
				stmt->ard = &stmt->i_ard;
			} else {
				/* "This attribute cannot be set to a descriptor handle that
				 * was implicitly allocated for another statement or to
				 * another descriptor handle that was implicitly set on the
				 * same statement; implicitly allocated descriptor handles
				 * cannot be associated with more than one statement or
				 * descriptor handle." */
				if (desc->alloc_type == SQL_DESC_ALLOC_AUTO) {
					ERRH(stmt, "source ARD (@0x%p) is implicit (alloc: %d).",
							desc, desc->alloc_type);
					RET_HDIAGS(stmt, SQL_STATE_HY017);
				} else {
					switch (desc->type) {
						case DESC_TYPE_ANON:
							desc->type = DESC_TYPE_ARD;
						case DESC_TYPE_ARD:
							break;
						default:
							// TODO: should this be allowed?
							/* this means a descriptor can not be changed from
							 * APD to ARD (IxD is also ruled out) */
							ERRH(stmt, "can't convert descriptor from type %d"
									" to ARD.", desc->type);
							RET_HDIAGS(stmt, SQL_STATE_HY024);
					}
					DBGH(stmt, "overwritting current ARD (@0x%p) with new "
							"value (@0x%p).", stmt->ard, desc);
					stmt->ard = desc;
				}
			}
			break;
		case SQL_ATTR_APP_PARAM_DESC:
			// FIXME: same logic for ARD as above (part of params passing)
			FIXME;
			break;

		case SQL_ATTR_IMP_ROW_DESC:
		case SQL_ATTR_IMP_PARAM_DESC:
			ERRH(stmt, "trying to set IxD (%d) descriptor (to @0x%p).",
					Attribute, ValuePtr);
			RET_HDIAGS(stmt, SQL_STATE_HY017);

		case SQL_ATTR_ROW_NUMBER:
			ERRH(stmt, "row number attribute is read-only.");
			RET_HDIAGS(stmt, SQL_STATE_HY024);

		case SQL_ATTR_METADATA_ID:
			DBGH(stmt, "setting metadata_id to: %llu", (uint64_t)ValuePtr);
			stmt->metadata_id = (SQLULEN)ValuePtr;
			break;

		case SQL_ATTR_ASYNC_ENABLE:
			ERRH(stmt, "no support for async API (setting param: %llu)",
					(uint64_t)ValuePtr);
			if ((SQLULEN)ValuePtr == SQL_ASYNC_ENABLE_ON) {
				RET_HDIAGS(stmt, SQL_STATE_HYC00);
			}
			break;
		case SQL_ATTR_ASYNC_STMT_EVENT:
		// case SQL_ATTR_ASYNC_STMT_PCALLBACK:
		// case SQL_ATTR_ASYNC_STMT_PCONTEXT:
			ERRH(stmt, "no support for async API (attr: %ld)", Attribute);
			RET_HDIAGS(stmt, SQL_STATE_S1118);

		case SQL_ATTR_MAX_LENGTH:
			ulen = (SQLULEN)ValuePtr;
			DBGH(stmt, "setting max_length to: %llu.", (uint64_t)ulen);
			if (ulen < ESODBC_LO_MAX_LENGTH) {
				WARNH(stmt, "MAX_LENGTH lower than min allowed (%d) -- "
						"correcting value.", ESODBC_LO_MAX_LENGTH);
				ulen = ESODBC_LO_MAX_LENGTH;
			} else if (ESODBC_UP_MAX_LENGTH && ESODBC_UP_MAX_LENGTH < ulen) {
				WARNH(stmt, "MAX_LENGTH higher than max allowed (%d) -- "
						"correcting value", ESODBC_UP_MAX_LENGTH);
				ulen = ESODBC_UP_MAX_LENGTH;
			}
			stmt->max_length = ulen;
			if (ulen != (SQLULEN)ValuePtr)
				RET_HDIAGS(stmt, SQL_STATE_01S02);
			break;

		case SQL_ATTR_QUERY_TIMEOUT:
			DBGH(stmt, "setting query timeout to: %llu.", (uint64_t)ValuePtr);
			stmt->query_timeout = (SQLULEN)ValuePtr;
			break;

		case SQL_ATTR_CURSOR_TYPE:
			DBGH(stmt, "setting cursor type: %llu.", (SQLUBIGINT)ValuePtr);
			if ((SQLULEN)ValuePtr != SQL_CURSOR_FORWARD_ONLY) {
				WARNH(stmt, "requested cursor_type substituted with "
						"forward-only (%lu).", SQL_CURSOR_FORWARD_ONLY);
				RET_HDIAGS(stmt, SQL_STATE_01S02);
			}
			break;

		case SQL_ATTR_NOSCAN:
			DBGH(stmt, "setting escape seq scanning: %llu -- NOOP.",
					(uint64_t)ValuePtr);
			/* nothing to do: the driver never scans the input, ESSQL processes
			 * the escape sequences */
			break;

		case SQL_ATTR_CONCURRENCY:
			DBGH(stmt, "setting concurrency: %llu.", (uint64_t)ValuePtr);
			if ((SQLULEN)ValuePtr != SQL_CONCUR_READ_ONLY) {
				WARNH(stmt, "requested concurrency substituted with "
						"read-only (%d).", SQL_CONCUR_READ_ONLY);
				RET_HDIAGS(stmt, SQL_STATE_01S02);
			}
			break;

		case SQL_ATTR_MAX_ROWS:
			DBGH(stmt, "setting max rows: %llu.", (uint64_t)ValuePtr);
			if ((SQLULEN)ValuePtr != 0) {
				WARNH(stmt, "requested max_rows substituted with 0.");
				RET_HDIAGS(stmt, SQL_STATE_01S02);
			}
			break;

		case SQL_ATTR_CURSOR_SENSITIVITY:
			DBGH(stmt, "setting cursor sensitivity: %llu.",
					(uint64_t)ValuePtr);
			if ((SQLULEN)ValuePtr != SQL_UNSPECIFIED) {
				ERRH(stmt, "driver supports forward-only cursors.");
				RET_HDIAGS(stmt, SQL_STATE_HYC00);
			}
			break;

		case SQL_ATTR_CURSOR_SCROLLABLE:
			DBGH(stmt, "setting scrollable cursor: %llu.", (uint64_t)ValuePtr);
			if ((SQLULEN)ValuePtr != SQL_NONSCROLLABLE) {
				ERRH(stmt, "driver supports only non-scrollable cursors.");
				RET_HDIAGS(stmt, SQL_STATE_HYC00);
			}
			break;

		case SQL_ATTR_RETRIEVE_DATA:
			DBGH(stmt, "setting data retrieving: %llu.", (uint64_t)ValuePtr);
			if ((SQLULEN)ValuePtr != SQL_RD_ON) {
				WARNH(stmt, "no fetching without data retrieval possible.");
				RET_HDIAGS(stmt, SQL_STATE_01S02);
			}
			break;

		/* SQL Server non-standard attributes */
		case 1226:
		case 1227:
		case 1228:
			ERRH(stmt, "non-standard attribute: %d.", Attribute);
			/* "Invalid attribute/option identifier" */
			RET_HDIAGS(stmt, SQL_STATE_HY092);

		default:
			// FIXME
			BUGH(stmt, "unknown Attribute: %d.", Attribute);
			RET_HDIAGS(stmt, SQL_STATE_HY092);
	}
	/*INDENT-ON*/

	return SQL_SUCCESS;
}