/* 
 * Legal Notice 
 * 
 * This document and associated source code (the "Work") is a part of a 
 * benchmark specification maintained by the TPC. 
 * 
 * The TPC reserves all right, title, and interest to the Work as provided 
 * under U.S. and international laws, including without limitation all patent 
 * and trademark rights therein. 
 * 
 * No Warranty 
 * 
 * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION 
 *     CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE 
 *     AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER 
 *     WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, 
 *     INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES, 
 *     DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR 
 *     PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF 
 *     WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE. 
 *     ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, 
 *     QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT 
 *     WITH REGARD TO THE WORK. 
 * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO 
 *     ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE 
 *     COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS 
 *     OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT, 
 *     INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY,
 *     OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT 
 *     RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD 
 *     ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. 
 * 
 * Contributors:
 * Gradient Systems
 */ 
#include "config.h"
#include "porting.h"
#include <stdio.h>
#include <assert.h>
#include <stdio.h>
#include "config.h"
#include "porting.h"
#include "dist.h"
#include "constants.h"
#include "genrand.h"
#include "columns.h"
#include "tdefs.h"
#include "error_msg.h"
#include "r_params.h"
#include "tdefs.h"
#include "tdef_functions.h"
#include "w_inventory.h"
#include "scaling.h"
#include "tpcds.idx.h"
#include "print.h"
#include "parallel.h"
#include "scd.h"


static struct SCALING_T {
   ds_key_t kBaseRowcount;
   ds_key_t kNextInsertValue;
   int	nUpdatePercentage;
   ds_key_t kDayRowcount[6];
} arRowcount[MAX_TABLE + 1];
static int arUpdateDates[6];
static int arInventoryUpdateDates[6];

static int arScaleVolume[9] =
   {1, 10, 100, 300, 1000, 3000, 10000, 30000, 100000};

void setUpdateScaling(int table);
int	row_skip(int tbl, ds_key_t count);

/*
* Routine: 
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
int
getScaleSlot(int nTargetGB)
{
   int i;

   for (i=0; nTargetGB > arScaleVolume[i]; i++);

   return(i);
}

/*
* Routine: LogScale(void)
* Purpose: use the command line volume target, in GB, to calculate the global rowcount multiplier
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions: 
* Side Effects: arRowcounts are set to the appropriate number of rows for the target scale factor
* TODO: None
*/
static ds_key_t
LogScale(int nTable, int nTargetGB)
{
	int nIndex = 1,
		nDelta,
      i;
	float fOffset;
	ds_key_t hgRowcount = 0;

	i = getScaleSlot(nTargetGB);

	nDelta = dist_weight(NULL, "rowcounts", nTable + 1, i + 1) - dist_weight(NULL, "rowcounts", nTable + 1, i);
	fOffset = (float)(nTargetGB - arScaleVolume[i - 1])/(float)(arScaleVolume[i] -  arScaleVolume[i - 1]);

	hgRowcount = (int)(fOffset * (float)nDelta);
	hgRowcount += dist_weight(NULL, "rowcounts", nTable + 1, nIndex);


	return(hgRowcount);
}

/*
* Routine: StaticScale(void)
* Purpose: use the command line volume target, in GB, to calculate the global rowcount multiplier
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions: 
* Side Effects: arRowcounts are set to the appropriate number of rows for the target scale factor
* TODO: None
*/
static ds_key_t
StaticScale(int nTable, int nTargetGB)
{
	return(dist_weight(NULL, "rowcounts", nTable + 1, 1));
}

/*
* Routine: LinearScale(void)
* Purpose: use the command line volume target, in GB, to calculate the global rowcount multiplier
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions: scale factors defined in rowcounts distribution define 1/10/100/1000/... GB 
*	with sufficient accuracy
* Side Effects: arRowcounts are set to the appropriate number of rows for the target scale factor
* TODO: None
*/
static ds_key_t
LinearScale(int nTable, int nTargetGB)
{
	int i;
	ds_key_t hgRowcount = 0;
	
	for (i=8; i >= 0; i--)	/* work from large scales down)*/
	{
		/*
		 * use the defined rowcounts to build up the target GB volume 
		 */
		while (nTargetGB >= arScaleVolume[i])
		{
			hgRowcount += dist_weight(NULL, "rowcounts", nTable + 1, i + 1);
			nTargetGB -= arScaleVolume[i];
		}
	}

	return(hgRowcount);
}
/*
* Routine: 
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
ds_key_t
getIDCount(int nTable)
{
   ds_key_t kRowcount,
      kUniqueCount;
   tdef *pTdef;
   
   kRowcount = get_rowcount(nTable);
   if (nTable >= PSEUDO_TABLE_START)
      return(kRowcount);
   pTdef = getSimpleTdefsByNumber(nTable);
   if (pTdef->flags & FL_TYPE_2)
   {
      kUniqueCount = (kRowcount / 6) * 3;
      switch (kRowcount % 6)
      {
      case 1:
         kUniqueCount += 1;
         break;
     case 2:
     case 3:
         kUniqueCount += 2;
         break;
     case 4:
     case 5:
         kUniqueCount += 3;
         break;
      }
      return(kUniqueCount);
   }
   else
   {
      return(kRowcount);
   }
}

/*
* Routine: get_rowcount(int table)
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: 20040820 jms Need to address special case scaling in a more general fashion
*/
ds_key_t
get_rowcount(int table)
{

	static int bScaleSet = 0,
      nScale;
	int nTable,
		nMultiplier,
		i,
		nBadScale = 0,
		nRowcountOffset = 0;
	tdef *pTdef;
	
	if (!bScaleSet)
	{		
		nScale = get_int("SCALE");
		if (nScale > 100000)
			ReportErrorNoLine(QERR_BAD_SCALE, NULL, 1);

		memset(arRowcount, 0, sizeof(long) * MAX_TABLE);
		for (nTable=CALL_CENTER; nTable <= MAX_TABLE; nTable++)
		{
			switch(nScale)
			{
			case 100000:
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset + 1, 9);
				break;
			case 30000:
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 8);
				break;
			case 10000:
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 7);
				break;
			case 3000:
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 6);
				break;
			case 1000:
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 5);
				break;
			case 300:
				nBadScale = QERR_BAD_SCALE;
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 4);
				break;
			case 100:
				nBadScale = QERR_BAD_SCALE;
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 3);
				break;
			case 10:
				nBadScale = QERR_BAD_SCALE;
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 2);
				break;
			case 1:
				nBadScale = QERR_QUALIFICATION_SCALE;
				arRowcount[nTable].kBaseRowcount = dist_weight(NULL, "rowcounts", nTable + nRowcountOffset  + 1, 1);
				break;
			default:
				nBadScale = QERR_BAD_SCALE;
				switch(dist_member(NULL, "rowcounts", nTable + 1, 3))
				{
				case 2:
					arRowcount[nTable].kBaseRowcount = LinearScale(nTable + nRowcountOffset , nScale);
					break;
				case 1:
					arRowcount[nTable].kBaseRowcount = StaticScale(nTable + nRowcountOffset , nScale);
					break;
				case 3:
					arRowcount[nTable].kBaseRowcount = LogScale(nTable + nRowcountOffset , nScale);
					break;
				}	/* switch(FL_SCALE_MASK) */
				break;
			} /* switch(nScale) */

			/* now adjust for the multiplier */
         nMultiplier = 1;
			if (nTable < PSEUDO_TABLE_START)
         {
            pTdef = getSimpleTdefsByNumber(nTable);
			   nMultiplier = (pTdef->flags & FL_TYPE_2)?2:1;
         }
			for (i=1; i <= dist_member(NULL, "rowcounts", nTable + 1, 2); i++)
				nMultiplier *= 10;
			arRowcount[nTable].kBaseRowcount *= nMultiplier;

      } /* for each table */

		
		if (nBadScale && !is_set("QUIET"))
			ReportErrorNoLine(nBadScale, NULL, 0);

		bScaleSet = 1;
	}
	
	if ((table == INVENTORY))
		return(sc_w_inventory(nScale));
	if ((table == S_INVENTORY))
		return(getIDCount(ITEM) * get_rowcount(WAREHOUSE) * 6);

	return(arRowcount[table].kBaseRowcount);

}

/*
* Routine: setUpdateDates
* Purpose: determine the dates for fact table updates
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
void
setUpdateDates(void)
{
   int nDay,
      nUpdate,
      i;
   date_t dtTemp;

   nUpdate = get_int("UPDATE");
   while (nUpdate--)
   {
      /* pick two adjacent days in the low density zone */
      arUpdateDates[0] = getSkewedJulianDate(calendar_low, 0);
      jtodt(&dtTemp, arUpdateDates[0]);
      dist_weight(&nDay, "calendar", day_number(&dtTemp) + 1, calendar_low);
      if (nDay)
         arUpdateDates[1] = arUpdateDates[0] + 1;
      else
         arUpdateDates[1] = arUpdateDates[0] - 1;

      /* 
       * pick the related Thursdays for inventory 
       * 1. shift first date to the Thursday in the current update week
       * 2. move forward/back to get into correct comparability zone
       * 3. set next date to next/prior Thursday based on comparability zone
       */
      jtodt(&dtTemp, arUpdateDates[0] + (4 - set_dow(&dtTemp)));
      dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_low);
      arInventoryUpdateDates[0] = dtTemp.julian;
      if (!nDay)
      {
        jtodt(&dtTemp, dtTemp.julian - 7);
        arInventoryUpdateDates[0] = dtTemp.julian;
        dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_low);
         if (!nDay)
            arInventoryUpdateDates[0] += 14;
      }

      arInventoryUpdateDates[1] = arInventoryUpdateDates[0] + 7;
      jtodt(&dtTemp, arInventoryUpdateDates[1]);
      dist_weight(&nDay, "calendar", day_number(&dtTemp) + 1, calendar_low);
      if (!nDay)
            arInventoryUpdateDates[1] -= 14;
     
      /* repeat for medium calendar zone */
      arUpdateDates[2] = getSkewedJulianDate(calendar_medium, 0);
      jtodt(&dtTemp, arUpdateDates[2]);
      dist_weight(&nDay, "calendar", day_number(&dtTemp) + 1, calendar_medium);
      if (nDay)
         arUpdateDates[3] = arUpdateDates[2] + 1;
      else
         arUpdateDates[3] = arUpdateDates[2] - 1;

      jtodt(&dtTemp, arUpdateDates[2] + (4 - set_dow(&dtTemp)));
      dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_medium);
      arInventoryUpdateDates[2] = dtTemp.julian;
      if (!nDay)
      {
        jtodt(&dtTemp, dtTemp.julian - 7);
        arInventoryUpdateDates[2] = dtTemp.julian;
        dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_medium);
         if (!nDay)
            arInventoryUpdateDates[2] += 14;
      }

      arInventoryUpdateDates[3] = arInventoryUpdateDates[2] + 7;
      jtodt(&dtTemp, arInventoryUpdateDates[3]);
      dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_medium);
      if (!nDay)
            arInventoryUpdateDates[3] -= 14;

      
      /* repeat for high calendar zone */
      arUpdateDates[4] = getSkewedJulianDate(calendar_high, 0);
      jtodt(&dtTemp, arUpdateDates[4]);
      dist_weight(&nDay, "calendar", day_number(&dtTemp) + 1, calendar_high);
      if (nDay)
         arUpdateDates[5] = arUpdateDates[4] + 1;
      else
         arUpdateDates[5] = arUpdateDates[4] - 1;

      jtodt(&dtTemp, arUpdateDates[4] + (4 - set_dow(&dtTemp)));
      dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_high);
      arInventoryUpdateDates[4] = dtTemp.julian;
      if (!nDay)
      {
        jtodt(&dtTemp, dtTemp.julian - 7);
        arInventoryUpdateDates[4] = dtTemp.julian;
        dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_high);
         if (!nDay)
            arInventoryUpdateDates[4] += 14;
      }

      arInventoryUpdateDates[5] = arInventoryUpdateDates[4] + 7;
      jtodt(&dtTemp, arInventoryUpdateDates[5]);
      dist_weight(&nDay, "calendar", day_number(&dtTemp), calendar_high);
      if (!nDay)
            arInventoryUpdateDates[5] -= 14;


   }
   
   /*
   * output the update dates for this update set 
   */
   openDeleteFile(1);
   for (i=0; i < 6; i += 2)
      print_delete(&arUpdateDates[i]);

   /*
    * inventory uses separate dates
    */
   openDeleteFile(2);
   for (i=0; i < 6; i += 2)
      print_delete(&arInventoryUpdateDates[i]);
   openDeleteFile(0);

   return;
}

/*
* Routine: 
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
int
getUpdateDate(int nTable, ds_key_t kRowcount)
{
   static int nIndex = 0,
      nLastTable = -1;

   if (nLastTable != nTable)
   {
      nLastTable = nTable;
      get_rowcount(nTable);
      nIndex = 0;
   }
   
   for (nIndex = 0; kRowcount > arRowcount[nTable].kDayRowcount[nIndex]; nIndex++)
      if (nIndex == 5) break;

   if (nTable == S_INVENTORY)
   {
      return(arInventoryUpdateDates[nIndex]);
   }
   else
    return(arUpdateDates[nIndex]);
}



/*
* Routine: getUpdateID(int nTable, ds_key_t *pDest)
* Purpose: select the primary key for an update set row
* Algorithm:
* Data Structures:
*
* Params:
* Returns: 1 if the row is new, 0 if it is reusing an existing ID
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: 20040326 jms getUpdateID() this MUST be updated for 64bit -- all usages use casts today
* TODO:	20060102 jms this will need to be looked at for parallelism at some point
*/
/*
int
getUpdateID(ds_key_t *pDest, int nTable, int nColumn)
{
	int bIsUpdate = 0,
		nTemp;
	
	if (genrand_integer(NULL, DIST_UNIFORM, 0, 99, 0, nColumn) < arRowcount[nTable].nUpdatePercentage)
	{
		bIsUpdate = 1;
		genrand_integer(&nTemp, DIST_UNIFORM, 1, (int)getIDCount(nTable), 0, nColumn);
		*pDest = (ds_key_t)nTemp;
	}
	else
	{
		*pDest = ++arRowcount[nTable].kNextInsertValue;
	}

	return(bIsUpdate);
}
*/


/*
* Routine: getSkewedJulianDate()
* Purpose: return a julian date based on the given skew and column
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
int
getSkewedJulianDate(int nWeight, int nColumn)
{
   int i;
   date_t Date;
   
   pick_distribution(&i, "calendar", 1, nWeight, nColumn);
   genrand_integer(&Date.year, DIST_UNIFORM, YEAR_MINIMUM, YEAR_MAXIMUM, 0, nColumn);
   dist_member(&Date.day, "calendar", i, 3);
   dist_member(&Date.month, "calendar", i, 5);
   return(dttoj(&Date));
}

/*
* Routine: initializeOrderUpdate()
* Purpose: skip over prior updates for the named table
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
/*
int
initializeOrderUpdates(int nParent, int nChild, int nIDColumn, int nDateColumn, int *pnOrderNumber)
{
   int i,
      nRowcount,
      nRowsRemaining,
      nStep = 0;
   date_t Date;


		*pnOrderNumber = 0;
      
      for (i=0; i < (get_int("UPDATE") - 1); i++)
		{
			nRowsRemaining = (int)get_rowcount(nParent);
         while (nRowsRemaining > 0)
         {
            nStep = nStep % 3;
            nStep += 1;
            Date.julian = getSkewedJulianDate((nStep++ % 3) + 8, nDateColumn);
            nRowcount = (int)dateScaling(getTableFromColumn(nIDColumn), Date.julian);
            *pnOrderNumber += nRowcount;
            row_skip(nParent, nRowcount);
            row_skip(nChild, LINES_PER_ORDER * nRowcount);
            nRowsRemaining -= nRowcount;
         }
		}

      return(nStep);
}
*/

/*
* Routine: dateScaling(int nTable, ds_key_t jDate)
* Purpose: determine the number of rows to build for a given date and fact table
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
ds_key_t
dateScaling(int nTable, ds_key_t jDate)
{
	static int bInit = 0;
	static dist_t *pDist;
	d_idx_t *pDistIndex;
	date_t Date;
	int nDateWeight = 1,
		nCalendarTotal,
		nDayWeight;
	ds_key_t kRowCount = -1;
   tdef *pTdef = getSimpleTdefsByNumber(nTable);

	if (!bInit)
	{
		pDistIndex = find_dist("calendar");
		pDist = pDistIndex->dist;
		if (!pDist)
			ReportError(QERR_NO_MEMORY, "dateScaling()", 1);
		bInit = 1;
	}

	jtodt(&Date, (int)jDate);
	
	switch(nTable)
	{
	case STORE_SALES:
	case CATALOG_SALES:
	case WEB_SALES:
      kRowCount = get_rowcount(nTable);
		nDateWeight = calendar_sales;
		break;
	case S_CATALOG_ORDER:
      kRowCount = get_rowcount(CATALOG_SALES);
		nDateWeight = calendar_sales;
		break;
	case S_PURCHASE:
      kRowCount = get_rowcount(STORE_SALES);
		nDateWeight = calendar_sales;
		break;
	case S_WEB_ORDER:
      kRowCount = get_rowcount(WEB_SALES);
		nDateWeight = calendar_sales;
		break;
	case S_INVENTORY:
   case INVENTORY:
		nDateWeight = calendar_uniform;
      kRowCount = get_rowcount(WAREHOUSE) * getIDCount(ITEM);
		break;
	default:
		ReportErrorNoLine(QERR_TABLE_NOP, pTdef->name, 1);
		break;
	}

	if (nTable != INVENTORY) /* inventory rowcount is uniform thorughout the year */
   {
      if (is_leap(Date.year))
		nDateWeight += 1;

	nCalendarTotal = dist_max(pDist, nDateWeight);
	nCalendarTotal *= 5;	/* assumes date range is 5 years */

	dist_weight(&nDayWeight, "calendar", day_number(&Date), nDateWeight);
	kRowCount *= nDayWeight;
   kRowCount += nCalendarTotal / 2;
	kRowCount /= nCalendarTotal;
   }

	return(kRowCount);
}

/*
* Routine: getUpdateBase(int nTable)
* Purpose: return the offset to the first order in this update set for a given table
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
ds_key_t
getUpdateBase(int nTable)
{
   return(arRowcount[nTable - S_BRAND].kNextInsertValue);
}

/*
* Routine: 
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
void
setUpdateScaling(int nTable)
{
   tdef *pTdef;
   int i,
      nBaseTable;
   ds_key_t kNewRowcount = 0;

   pTdef = getSimpleTdefsByNumber(nTable);
   if (!(pTdef->flags & FL_SOURCE_DDL) ||
      !(pTdef->flags & FL_DATE_BASED) ||
      (pTdef->flags & FL_NOP))
      return;

   switch(nTable)
   {
   case S_PURCHASE: nBaseTable = STORE_SALES; break;
   case S_CATALOG_ORDER: nBaseTable = CATALOG_SALES; break;
   case S_WEB_ORDER: nBaseTable = WEB_SALES; break;
   case S_INVENTORY: nBaseTable = INVENTORY; break;
   default:
      fprintf(stderr, "ERROR: Invalid table in setUpdateScaling\n");
      exit(1);
      break;
   }
   
   arRowcount[nTable].kNextInsertValue = arRowcount[nTable].kBaseRowcount;

   for (i=0; i < 6; i++)
   {
      kNewRowcount += dateScaling(nBaseTable, arUpdateDates[i]);
      arRowcount[nTable].kDayRowcount[i] = kNewRowcount;
   }

   arRowcount[nTable].kBaseRowcount = kNewRowcount;
   arRowcount[nTable].kNextInsertValue += kNewRowcount * (get_int("update") - 1);

   return;
}
