mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-26 06:43:04 +01:00
358 lines
8.9 KiB
C++
358 lines
8.9 KiB
C++
/*
|
|
* PROGRAM: JRD Command Oriented Query Language
|
|
* MODULE: report.c
|
|
* DESCRIPTION: Report writer runtime control
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include "../qli/dtr.h"
|
|
#include "../qli/exe.h"
|
|
#include "../qli/compile.h"
|
|
#include "../qli/report.h"
|
|
#include "../qli/all_proto.h"
|
|
#include "../qli/eval_proto.h"
|
|
#include "../qli/exe_proto.h"
|
|
#include "../qli/forma_proto.h"
|
|
#include "../qli/mov_proto.h"
|
|
#include "../qli/repor_proto.h"
|
|
|
|
static void bottom_break(BRK, PRT);
|
|
static void increment_break(BRK);
|
|
static void initialize_break(BRK);
|
|
static int test_break(BRK, RPT, QLI_MSG);
|
|
static void top_break(BRK, PRT);
|
|
static void top_of_page(PRT, BOOLEAN);
|
|
|
|
#define SWAP(a,b) {temp = a; a = b; b = temp;}
|
|
|
|
|
|
void RPT_report( QLI_NOD loop)
|
|
{
|
|
/**************************************
|
|
*
|
|
* R P T _ r e p o r t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a FOR loop. This may require that a request get
|
|
* started, a message sent, and a message received for each
|
|
* record. At the other end of the spectrum, there may be
|
|
* absolutely nothing to do.
|
|
*
|
|
**************************************/
|
|
RPT report;
|
|
QLI_NOD node;
|
|
QLI_REQ request;
|
|
QLI_MSG message;
|
|
DSC *desc;
|
|
BRK control;
|
|
STR string;
|
|
PRT print;
|
|
UCHAR *temp;
|
|
|
|
/* Get to actual report node */
|
|
|
|
node = loop->nod_arg[e_for_statement];
|
|
report = (RPT) node->nod_arg[e_prt_list];
|
|
print = (PRT) node->nod_arg[e_prt_output];
|
|
print->prt_new_page = (int(*)()) top_of_page;
|
|
print->prt_page_number = 0;
|
|
|
|
/* Get to actual report node */
|
|
|
|
/* If there is a request associated with the loop, start it and possibly
|
|
send a message slong with it. */
|
|
|
|
if (request = (QLI_REQ) loop->nod_arg[e_for_request])
|
|
EXEC_start_request(request, (QLI_MSG) loop->nod_arg[e_for_send]);
|
|
else if (message = (QLI_MSG) loop->nod_arg[e_for_send])
|
|
EXEC_send(message);
|
|
|
|
/* Receive messages in a loop until the end of file field comes up
|
|
true. */
|
|
|
|
message = (QLI_MSG) loop->nod_arg[e_for_receive];
|
|
|
|
/* Get the first record of the record. If there isn't anything,
|
|
don't worry about anything. */
|
|
|
|
desc = EXEC_receive(message, (PAR) loop->nod_arg[e_for_eof]);
|
|
if (*(USHORT *) desc->dsc_address)
|
|
return;
|
|
|
|
if (!report->rpt_buffer) {
|
|
string = (STR) ALLOCDV(type_str, message->msg_length);
|
|
report->rpt_buffer = (UCHAR *) string->str_data;
|
|
}
|
|
|
|
MOVQ_fast((SCHAR*) message->msg_buffer, (SCHAR*) report->rpt_buffer,
|
|
(SLONG) message->msg_length);
|
|
|
|
if (control = report->rpt_top_rpt)
|
|
FMT_print((QLI_NOD) control->brk_line, print);
|
|
|
|
top_of_page(print, TRUE);
|
|
|
|
initialize_break(report->rpt_bottom_breaks);
|
|
initialize_break(report->rpt_bottom_page);
|
|
initialize_break(report->rpt_bottom_rpt);
|
|
|
|
/* Force TOP breaks for all fields */
|
|
|
|
for (control = report->rpt_top_breaks; control;
|
|
control = control->brk_next) FMT_print((QLI_NOD) control->brk_line, print);
|
|
|
|
for (;;) {
|
|
/* Check for bottom breaks. If we find one, force all lower breaks. */
|
|
|
|
for (control = report->rpt_bottom_breaks; control;
|
|
control = control->brk_next)
|
|
if (test_break(control, report, message)) {
|
|
SWAP(message->msg_buffer, report->rpt_buffer);
|
|
bottom_break(control, print);
|
|
SWAP(message->msg_buffer, report->rpt_buffer);
|
|
initialize_break(control);
|
|
break;
|
|
}
|
|
|
|
if (print->prt_lines_remaining <= 0)
|
|
top_of_page(print, FALSE);
|
|
|
|
/* Now check for top breaks. */
|
|
|
|
for (control = report->rpt_top_breaks; control;
|
|
control = control->brk_next)
|
|
if (test_break(control, report, message)) {
|
|
top_break(control, print);
|
|
break;
|
|
}
|
|
|
|
/* Increment statisticals and print detail line, if any */
|
|
|
|
increment_break(report->rpt_bottom_breaks);
|
|
increment_break(report->rpt_bottom_page);
|
|
increment_break(report->rpt_bottom_rpt);
|
|
|
|
if (node = report->rpt_detail_line)
|
|
FMT_print(node, print);
|
|
|
|
/* Get the next record. If we're at end, we're almost done. */
|
|
|
|
SWAP(message->msg_buffer, report->rpt_buffer);
|
|
desc = EXEC_receive(message, (PAR) loop->nod_arg[e_for_eof]);
|
|
if (*(USHORT *) desc->dsc_address)
|
|
break;
|
|
}
|
|
|
|
/* Force BOTTOM breaks for all fields */
|
|
|
|
SWAP(message->msg_buffer, report->rpt_buffer);
|
|
bottom_break(report->rpt_bottom_breaks, print);
|
|
bottom_break(report->rpt_bottom_rpt, print);
|
|
|
|
if (control = report->rpt_bottom_page)
|
|
FMT_print((QLI_NOD) control->brk_line, print);
|
|
}
|
|
|
|
|
|
static void bottom_break( BRK control, PRT print)
|
|
{
|
|
/**************************************
|
|
*
|
|
* b o t t o m _ b r e a k
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Force all lower breaks then take break.
|
|
*
|
|
**************************************/
|
|
LLS stack;
|
|
|
|
if (!control)
|
|
return;
|
|
|
|
if (control->brk_next)
|
|
bottom_break(control->brk_next, print);
|
|
|
|
for (stack = control->brk_statisticals; stack; stack = stack->lls_next)
|
|
EVAL_break_compute((QLI_NOD) stack->lls_object);
|
|
|
|
FMT_print((QLI_NOD) control->brk_line, print);
|
|
}
|
|
|
|
|
|
static void increment_break( BRK control)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i n c r e m e n t _ b r e a k
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Toss another record into running computations.
|
|
*
|
|
**************************************/
|
|
LLS stack;
|
|
|
|
for (; control; control = control->brk_next)
|
|
for (stack = control->brk_statisticals; stack;
|
|
stack = stack->lls_next) EVAL_break_increment((QLI_NOD) stack->lls_object);
|
|
}
|
|
|
|
|
|
static void initialize_break( BRK control)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i n i t i a l i z e _ b r e a k
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a control break.
|
|
*
|
|
**************************************/
|
|
LLS stack;
|
|
|
|
for (; control; control = control->brk_next)
|
|
for (stack = control->brk_statisticals; stack;
|
|
stack = stack->lls_next) EVAL_break_init((QLI_NOD) stack->lls_object);
|
|
}
|
|
|
|
|
|
static int test_break( BRK control, RPT report, QLI_MSG message)
|
|
{
|
|
/**************************************
|
|
*
|
|
* t e s t _ b r e a k
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Check to see if there has been a control break for an expression.
|
|
*
|
|
**************************************/
|
|
DSC desc1, desc2, *ptr1, *ptr2;
|
|
UCHAR *p1, *p2;
|
|
USHORT l;
|
|
|
|
/* Evaluate the two versions of the expression */
|
|
|
|
if (ptr1 = EVAL_value((QLI_NOD) control->brk_field))
|
|
desc1 = *ptr1;
|
|
|
|
p1 = message->msg_buffer;
|
|
message->msg_buffer = report->rpt_buffer;
|
|
|
|
if (ptr2 = EVAL_value((QLI_NOD) control->brk_field))
|
|
desc2 = *ptr2;
|
|
|
|
message->msg_buffer = p1;
|
|
|
|
/* Check for consistently missing */
|
|
|
|
if (!ptr1 || !ptr2)
|
|
return (ptr1 != ptr2);
|
|
|
|
/* Both fields are present. Check values. Luckily, there's no need
|
|
to worry about datatypes. */
|
|
|
|
p1 = desc1.dsc_address;
|
|
p2 = desc2.dsc_address;
|
|
l = desc1.dsc_length;
|
|
|
|
if (desc1.dsc_dtype == dtype_varying)
|
|
l = 2 + *(USHORT *) p1;
|
|
|
|
if (l)
|
|
do
|
|
if (*p1++ != *p2++)
|
|
return TRUE;
|
|
while (--l);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void top_break( BRK control, PRT print)
|
|
{
|
|
/**************************************
|
|
*
|
|
* t o p _ b r e a k
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a control break.
|
|
*
|
|
**************************************/
|
|
LLS stack;
|
|
|
|
for (; control; control = control->brk_next) {
|
|
for (stack = control->brk_statisticals; stack;
|
|
stack = stack->lls_next) EVAL_break_compute((QLI_NOD) stack->lls_object);
|
|
FMT_print((QLI_NOD) control->brk_line, print);
|
|
}
|
|
}
|
|
|
|
|
|
static void top_of_page( PRT print, BOOLEAN first_flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* t o p _ o f _ p a g e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Handle top of page condition.
|
|
*
|
|
**************************************/
|
|
RPT report;
|
|
BRK control;
|
|
|
|
++print->prt_page_number;
|
|
report = print->prt_report;
|
|
|
|
if (!first_flag) {
|
|
if (control = report->rpt_bottom_page)
|
|
FMT_print((QLI_NOD) control->brk_line, print);
|
|
FMT_put("\f", print);
|
|
}
|
|
|
|
print->prt_lines_remaining = print->prt_lines_per_page;
|
|
|
|
if (control = report->rpt_top_page)
|
|
FMT_print((QLI_NOD) control->brk_line, print);
|
|
else if (report->rpt_column_header) {
|
|
if (report->rpt_header)
|
|
FMT_put(report->rpt_header, print);
|
|
if (report->rpt_column_header)
|
|
FMT_put(report->rpt_column_header, print);
|
|
}
|
|
|
|
if (report->rpt_bottom_page)
|
|
initialize_break(report->rpt_bottom_page);
|
|
}
|