Example #1
0
void ExtEngineManager::Trigger::execute(thread_db* tdbb, ExternalTrigger::Action action,
	record_param* oldRpb, record_param* newRpb) const
{
	EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
	ContextManager<ExternalTrigger> ctxManager(tdbb, attInfo, trigger,
		CallerName(obj_trigger, trg->name));

	// ASF: Using Array instead of HalfStaticArray to not need to do alignment hacks here.
	Array<UCHAR> oldMsg;
	Array<UCHAR> newMsg;

	if (oldRpb)
		setValues(tdbb, oldMsg, oldRpb);

	if (newRpb)
		setValues(tdbb, newMsg, newRpb);

	{	// scope
		Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);

		LocalStatus status;
		trigger->execute(&status, attInfo->context, action,
			(oldMsg.hasData() ? oldMsg.begin() : NULL), (newMsg.hasData() ? newMsg.begin() : NULL));
		status.check();
	}

	if (newRpb)
	{
		// Move data back from the message to the record.

		Record* record = newRpb->rpb_record;
		UCHAR* p = newMsg.begin();

		for (unsigned i = 0; i < format->fmt_count / 2u; ++i)
		{
			USHORT fieldPos = fieldsPos[i];

			dsc target;
			bool readonly = !EVL_field(newRpb->rpb_relation, record, fieldPos, &target) &&
				target.dsc_address && !(target.dsc_flags & DSC_null);

			if (!readonly)
			{
				SSHORT* nullSource = (SSHORT*) (p + (IPTR) format->fmt_desc[i * 2 + 1].dsc_address);

				if (*nullSource == 0)
				{
					dsc source = format->fmt_desc[i * 2];
					source.dsc_address += (IPTR) p;
					MOV_move(tdbb, &source, &target);
					record->clearNull(fieldPos);
				}
				else
					record->setNull(fieldPos);
			}
		}
	}
}
Example #2
0
bool AggregatedStream::getRecord(thread_db* tdbb) const
{
	if (--tdbb->tdbb_quantum < 0)
		JRD_reschedule(tdbb, 0, true);

	jrd_req* const request = tdbb->getRequest();
	record_param* const rpb = &request->req_rpb[m_stream];
	Impure* const impure = request->getImpure<Impure>(m_impure);

	if (!(impure->irsb_flags & irsb_open))
	{
		rpb->rpb_number.setValid(false);
		return false;
	}

	if (m_bufferedStream)	// Is that a window stream?
	{
		const FB_UINT64 position = m_bufferedStream->getPosition(request);

		if (impure->pending == 0)
		{
			if (impure->state == STATE_PENDING)
			{
				if (!m_bufferedStream->getRecord(tdbb))
					fb_assert(false);
			}

			impure->state = evaluateGroup(tdbb, impure->state);

			if (impure->state == STATE_PROCESS_EOF)
			{
				rpb->rpb_number.setValid(false);
				return false;
			}

			impure->pending = m_bufferedStream->getPosition(request) - position -
				(impure->state == STATE_EOF_FOUND ? 0 : 1);
			m_bufferedStream->locate(tdbb, position);
		}

		if (m_winPassSources.hasData())
		{
			SlidingWindow window(tdbb, m_bufferedStream, m_group, request);
			dsc* desc;

			const NestConst<ValueExprNode>* const sourceEnd = m_winPassSources.end();

			for (const NestConst<ValueExprNode>* source = m_winPassSources.begin(),
					*target = m_winPassTargets.begin();
				 source != sourceEnd;
				 ++source, ++target)
			{
				const AggNode* aggNode = (*source)->as<AggNode>();

				const FieldNode* field = (*target)->as<FieldNode>();
				const USHORT id = field->fieldId;
				Record* record = request->req_rpb[field->fieldStream].rpb_record;

				desc = aggNode->winPass(tdbb, request, &window);

				if (!desc)
					record->setNull(id);
				else
				{
					MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target));
					record->clearNull(id);
				}
			}
		}

		if (impure->pending > 0)
			--impure->pending;

		if (!m_bufferedStream->getRecord(tdbb))
		{
			rpb->rpb_number.setValid(false);
			return false;
		}

		// If there is no group, we should reassign the map items.
		if (!m_group)
		{
			const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();

			for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
					*target = m_map->targetList.begin();
				 source != sourceEnd;
				 ++source, ++target)
			{
				const AggNode* aggNode = (*source)->as<AggNode>();

				if (!aggNode)
					EXE_assignment(tdbb, *source, *target);
			}
		}
	}
	else
	{
		impure->state = evaluateGroup(tdbb, impure->state);

		if (impure->state == STATE_PROCESS_EOF)
		{
			rpb->rpb_number.setValid(false);
			return false;
		}
	}

	rpb->rpb_number.setValid(true);
	return true;
}
Example #3
0
// Compute the next aggregated record of a value group. evlGroup is driven by, and returns, a state
// variable.
AggregatedStream::State AggregatedStream::evaluateGroup(thread_db* tdbb, State state) const
{
	jrd_req* const request = tdbb->getRequest();

	if (--tdbb->tdbb_quantum < 0)
		JRD_reschedule(tdbb, 0, true);

	Impure* const impure = request->getImpure<Impure>(m_impure);

	// if we found the last record last time, we're all done

	if (state == STATE_EOF_FOUND)
		return STATE_PROCESS_EOF;

	try
	{
		const NestConst<ValueExprNode>* const sourceEnd = m_map->sourceList.end();

		// If there isn't a record pending, open the stream and get one

		if (!m_order || state == STATE_PROCESS_EOF || state == STATE_GROUPING)
		{
			// Initialize the aggregate record

			for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
					*target = m_map->targetList.begin();
				 source != sourceEnd;
				 ++source, ++target)
			{
				const AggNode* aggNode = (*source)->as<AggNode>();

				if (aggNode)
					aggNode->aggInit(tdbb, request);
				else if ((*source)->is<LiteralNode>())
					EXE_assignment(tdbb, *source, *target);
			}
		}

		if (state == STATE_PROCESS_EOF || state == STATE_GROUPING)
		{
			if (!m_next->getRecord(tdbb))
			{
				if (m_group)
				{
					finiDistinct(tdbb, request);
					return STATE_PROCESS_EOF;
				}

				state = STATE_EOF_FOUND;
			}
		}

		cacheValues(tdbb, request, m_group, 0);

		if (state != STATE_EOF_FOUND)
			cacheValues(tdbb, request, m_order, (m_group ? m_group->getCount() : 0));

		// Loop thru records until either a value change or EOF

		while (state != STATE_EOF_FOUND)
		{
			state = STATE_PENDING;

			// go through and compute all the aggregates on this record

			for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
					*target = m_map->targetList.begin();
				 source != sourceEnd;
				 ++source, ++target)
			{
				const AggNode* aggNode = (*source)->as<AggNode>();

				if (aggNode)
				{
					if (aggNode->aggPass(tdbb, request))
					{
						// If a max or min has been mapped to an index, then the first record is the EOF.
						if (aggNode->indexed)
							state = STATE_EOF_FOUND;
					}
				}
				else
					EXE_assignment(tdbb, *source, *target);
			}

			if (state != STATE_EOF_FOUND && m_next->getRecord(tdbb))
			{
				// In the case of a group by, look for a change in value of any of
				// the columns; if we find one, stop aggregating and return what we have.

				if (lookForChange(tdbb, request, m_group, 0))
				{
					if (m_order)
						state = STATE_GROUPING;
					break;
				}

				if (lookForChange(tdbb, request, m_order, (m_group ? m_group->getCount() : 0)))
					break;
			}
			else
				state = STATE_EOF_FOUND;
		}

		// Finish up any residual computations and get out

		for (const NestConst<ValueExprNode>* source = m_map->sourceList.begin(),
				*target = m_map->targetList.begin();
			 source != sourceEnd;
			 ++source, ++target)
		{
			const AggNode* aggNode = (*source)->as<AggNode>();

			if (aggNode)
			{
				const FieldNode* field = (*target)->as<FieldNode>();
				const USHORT id = field->fieldId;
				Record* record = request->req_rpb[field->fieldStream].rpb_record;

				dsc* desc = aggNode->execute(tdbb, request);
				if (!desc || !desc->dsc_dtype)
					record->setNull(id);
				else
				{
					MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target));
					record->clearNull(id);
				}
			}
		}
	}
	catch (const Exception&)
	{
		finiDistinct(tdbb, request);
		throw;
	}

	return state;
}
Example #4
0
// Perform an assignment.
void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bool from_null,
	const ValueExprNode* missing_node, const ValueExprNode* missing2_node)
{
	SET_TDBB(tdbb);
	jrd_req* request = tdbb->getRequest();

	// Get descriptors of receiving and sending fields/parameters, variables, etc.

	dsc* missing = NULL;
	if (missing_node)
		missing = EVL_expr(tdbb, request, missing_node);

	// Get descriptor of target field/parameter/variable, etc.
	DSC* to_desc = EVL_assign_to(tdbb, to);

	request->req_flags &= ~req_null;

	// NS: If we are assigning to NULL, we finished.
	// This functionality is currently used to allow calling UDF routines
	// without assigning resulting value anywhere.
	if (!to_desc)
		return;

	SSHORT null = from_null ? -1 : 0;

	if (!null && missing && MOV_compare(missing, from_desc) == 0)
		null = -1;

	USHORT* impure_flags = NULL;
	const ParameterNode* toParam;
	const VariableNode* toVar;

	if ((toParam = ExprNode::as<ParameterNode>(to)))
	{
		const MessageNode* message = toParam->message;

		if (toParam->argInfo)
		{
			EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, toParam->argNumber),
				toParam->argInfo, from_desc, null == -1);
		}

		impure_flags = request->getImpure<USHORT>(
			message->impureFlags + (sizeof(USHORT) * toParam->argNumber));
	}
	else if ((toVar = ExprNode::as<VariableNode>(to)))
	{
		if (toVar->varInfo)
		{
			EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId),
				toVar->varInfo, from_desc, null == -1);
		}

		impure_flags = &request->getImpure<impure_value>(
			toVar->varDecl->impureOffset)->vlu_flags;
	}

	if (impure_flags != NULL)
		*impure_flags |= VLU_checked;

	// If the value is non-missing, move/convert it.  Otherwise fill the
	// field with appropriate nulls.
	dsc temp;

	if (!null)
	{
		// if necessary and appropriate, use the indicator variable

		if (toParam && toParam->argIndicator)
		{
			dsc* indicator = EVL_assign_to(tdbb, toParam->argIndicator);
			temp.dsc_dtype = dtype_short;
			temp.dsc_length = sizeof(SSHORT);
			temp.dsc_scale = 0;
			temp.dsc_sub_type = 0;

			SSHORT len;

			if ((from_desc->dsc_dtype <= dtype_varying) && (to_desc->dsc_dtype <= dtype_varying) &&
				(TEXT_LEN(from_desc) > TEXT_LEN(to_desc)))
			{
				len = TEXT_LEN(from_desc);
			}
			else
				len = 0;

			temp.dsc_address = (UCHAR *) &len;
			MOV_move(tdbb, &temp, indicator);

			if (len)
			{
				temp = *from_desc;
				temp.dsc_length = TEXT_LEN(to_desc);

				if (temp.dsc_dtype == dtype_cstring)
					temp.dsc_length += 1;
				else if (temp.dsc_dtype == dtype_varying)
					temp.dsc_length += 2;

				from_desc = &temp;
			}
		}

		// Validate range for datetime values

		if (DTYPE_IS_DATE(from_desc->dsc_dtype))
		{
			switch (from_desc->dsc_dtype)
			{
				case dtype_sql_date:
					if (!Firebird::TimeStamp::isValidDate(*(GDS_DATE*) from_desc->dsc_address))
					{
						ERR_post(Arg::Gds(isc_date_range_exceeded));
					}
					break;

				case dtype_sql_time:
					if (!Firebird::TimeStamp::isValidTime(*(GDS_TIME*) from_desc->dsc_address))
					{
						ERR_post(Arg::Gds(isc_time_range_exceeded));
					}
					break;

				case dtype_timestamp:
					if (!Firebird::TimeStamp::isValidTimeStamp(*(GDS_TIMESTAMP*) from_desc->dsc_address))
					{
						ERR_post(Arg::Gds(isc_datetime_range_exceeded));
					}
					break;

				default:
					fb_assert(false);
			}
		}

		if (DTYPE_IS_BLOB_OR_QUAD(from_desc->dsc_dtype) || DTYPE_IS_BLOB_OR_QUAD(to_desc->dsc_dtype))
		{
			// ASF: Don't let MOV_move call blb::move because MOV
			// will not pass the destination field to blb::_move.
			blb::move(tdbb, from_desc, to_desc, to);
		}
		else if (!DSC_EQUIV(from_desc, to_desc, false))
		{
			MOV_move(tdbb, from_desc, to_desc);
		}
		else if (from_desc->dsc_dtype == dtype_short)
		{
			*((SSHORT*) to_desc->dsc_address) = *((SSHORT*) from_desc->dsc_address);
		}
		else if (from_desc->dsc_dtype == dtype_long)
		{
			*((SLONG*) to_desc->dsc_address) = *((SLONG*) from_desc->dsc_address);
		}
		else if (from_desc->dsc_dtype == dtype_int64)
		{
			*((SINT64*) to_desc->dsc_address) = *((SINT64*) from_desc->dsc_address);
		}
		else
		{
			memcpy(to_desc->dsc_address, from_desc->dsc_address, from_desc->dsc_length);
		}

		to_desc->dsc_flags &= ~DSC_null;
	}
	else
	{
		if (missing2_node && (missing = EVL_expr(tdbb, request, missing2_node)))
			MOV_move(tdbb, missing, to_desc);
		else
			memset(to_desc->dsc_address, 0, to_desc->dsc_length);

		to_desc->dsc_flags |= DSC_null;
	}

	// Handle the null flag as appropriate for fields and message arguments.


	const FieldNode* toField = ExprNode::as<FieldNode>(to);
	if (toField)
	{
		Record* record = request->req_rpb[toField->fieldStream].rpb_record;

		if (null)
			record->setNull(toField->fieldId);
		else
			record->clearNull(toField->fieldId);
	}
	else if (toParam && toParam->argFlag)
	{
		to_desc = EVL_assign_to(tdbb, toParam->argFlag);

		// If the null flag is a string with an effective length of one,
		// then -1 will not fit.  Therefore, store 1 instead.

		if (null && to_desc->dsc_dtype <= dtype_varying)
		{
			USHORT minlen;

			switch (to_desc->dsc_dtype)
			{
			case dtype_text:
				minlen = 1;
				break;
			case dtype_cstring:
				minlen = 2;
				break;
			case dtype_varying:
				minlen = 3;
				break;
			}

			if (to_desc->dsc_length <= minlen)
				null = 1;
		}

		temp.dsc_dtype = dtype_short;
		temp.dsc_length = sizeof(SSHORT);
		temp.dsc_scale = 0;
		temp.dsc_sub_type = 0;
		temp.dsc_address = (UCHAR*) &null;
		MOV_move(tdbb, &temp, to_desc);

		if (null && toParam->argIndicator)
		{
			to_desc = EVL_assign_to(tdbb, toParam->argIndicator);
			MOV_move(tdbb, &temp, to_desc);
		}
	}
}