mariadb/storage/connect/tabext.cpp
Olivier Bertrand e049f92392 Squashed commit of connect/10.0:
commit ac275d0b4ad (connect/10.0)
Author: Olivier Bertrand <bertrandop@gmail.com>
Date:   Wed Mar 27 12:46:20 2019 +0100

    Comment out unrecognized command line options: Modified CMakeLists.txt

commit 592f1f75ad6
Author: Olivier Bertrand <bertrandop@gmail.com>
Date:   Tue Mar 26 19:52:33 2019 +0100

    Replace Command not recognized by CMake modified: CMakeLists.txt

commit 00f72199b16
Author: Olivier Bertrand <bertrandop@gmail.com>
Date:   Tue Mar 26 18:15:08 2019 +0100

    - Fix MDEV-15793: Server crash in PlugCloseFile with sql_mode=''
      Fixed by replacing sprinf by snprintf in ShowValue to avoid
      buffer overflow. It nows always use a buffer and returns int.
      modified:   storage/connect/tabdos.cpp
      modified:   storage/connect/tabfmt.cpp
      modified:   storage/connect/value.cpp
      modified:   storage/connect/value.h

    - Fix MDEV-18292: CONNECT Engine JDBC not able to issue
      simple UPDATE statement from trigger or stored procedure
      Was not fixed when the same table was called several times
      with different modes. Fixed by checking if a new statement
      is compatible in the start_stmt function. It nows do the
      same checks than external_lock.
      modified:   storage/connect/ha_connect.cc
      modified:   storage/connect/ha_connect.h

    - typo
      modified:   storage/connect/user_connect.cc

    - Fix GetTableName that returned wrong value under Windows
      modified:   storage/connect/ha_connect.cc

    - Fix MDEV-13136: enhance CREATE SERVER MyServerName
      FOREIGN DATA WRAPPER to work with CONNECT engine
      modified:   storage/connect/tabjdbc.cpp

    - Add a function to retrieve User variable value (DEVELOPMENT only)
      modified:   storage/connect/ha_connect.cc
      modified:   storage/connect/jsonudf.cpp
      modified:   storage/connect/jsonudf.h
      modified:   storage/connect/tabjdbc.cpp

    - Fix MDEV-18192: CONNECT Engine JDBC not able to issue
      simple UPDATE statement from trigger or stored procedure
      modified:   storage/connect/tabext.cpp
      modified:   storage/connect/tabext.h
      modified:   storage/connect/tabjdbc.cpp

    - Enable CONNECT tables to have triggers
      Update version number
      modified:   storage/connect/ha_connect.cc

    - Make user and password defined in CREATE TABLE have precedence on
      the ones specified in a Federated Server.
      modified:   storage/connect/tabjdbc.cpp

    - JSONColumns: Copy locally constant strings to fix error in OEM modules
      modified:   storage/connect/tabjson.cpp

commit 99de7f4e486
Author: Olivier Bertrand <bertrandop@gmail.com>
Date:   Sun Jan 27 15:16:15 2019 +0100

    - Fix MDEV-18192: CONNECT Engine JDBC not able to issue
      simple UPDATE statement from trigger or stored procedure
      modified:   storage/connect/tabext.cpp
      modified:   storage/connect/tabext.h
      modified:   storage/connect/tabjdbc.cpp

    - Enable CONNECT tables to have triggers
      Update version number
      modified:   storage/connect/ha_connect.cc

    - Make user and password defined in CREATE TABLE have precedence on
      the ones specified in a Federated Server.
      modified:   storage/connect/tabjdbc.cpp

    - JSONColumns: Copy locally constant strings to fix error in OEM modules
      modified:   storage/connect/tabjson.cpp
2019-04-26 16:49:25 +02:00

720 lines
20 KiB
C++

/************* Tabext C++ Functions Source Code File (.CPP) ************/
/* Name: TABEXT.CPP Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2017 - 2019 */
/* */
/* This file contains the TBX, TDB and OPJOIN classes functions. */
/***********************************************************************/
/***********************************************************************/
/* Include relevant MariaDB header file. */
/***********************************************************************/
#define MYSQL_SERVER 1
#include "my_global.h"
#include "sql_class.h"
#include "sql_servers.h"
#include "sql_string.h"
#if !defined(__WIN__)
#include "osutil.h"
#endif
/***********************************************************************/
/* Include required application header files */
/* global.h is header containing all global Plug declarations. */
/* plgdbsem.h is header containing the DB applic. declarations. */
/* xobject.h is header containing XOBJECT derived classes declares. */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "xtable.h"
#include "tabext.h"
#include "ha_connect.h"
/* -------------------------- Class CONDFIL -------------------------- */
/***********************************************************************/
/* CONDFIL Constructor. */
/***********************************************************************/
CONDFIL::CONDFIL(uint idx, AMT type)
{
//Cond = cond;
Idx = idx;
Type = type;
Op = OP_XX;
Cmds = NULL;
Alist = NULL;
All = true;
Bd = false;
Hv = false;
Body = NULL,
Having = NULL;
} // end of CONDFIL constructor
/***********************************************************************/
/* Make and allocate the alias list. */
/***********************************************************************/
int CONDFIL::Init(PGLOBAL g, PHC hc)
{
PTOS options = hc->GetTableOptionStruct();
char *p, *cn, *cal, *alt = NULL;
int rc = RC_OK;
bool h;
if (options)
alt = (char*)GetListOption(g, "Alias", options->oplist, NULL);
while (alt) {
if (!(p = strchr(alt, '='))) {
strcpy(g->Message, "Invalid alias list");
rc = RC_FX;
break;
} // endif !p
cal = alt; // Alias
*p++ = 0;
if ((h = *p == '*')) {
rc = RC_INFO;
p++;
} // endif h
cn = p; // Remote column name
if ((alt = strchr(p, ';')))
*alt++ = 0;
if (*cn == 0)
cn = alt;
Alist = new(g) ALIAS(Alist, cn, cal, h);
} // endwhile alt
return rc;
} // end of Init
/***********************************************************************/
/* Make and allocate the alias list. */
/***********************************************************************/
const char *CONDFIL::Chk(const char *fln, bool *h)
{
for (PAL pal = Alist; pal; pal = pal->Next)
if (!stricmp(fln, pal->Alias)) {
*h = pal->Having;
return pal->Name;
} // endif fln
*h = false;
return fln;
} // end of Chk
/* --------------------------- Class EXTDEF -------------------------- */
/***********************************************************************/
/* EXTDEF Constructor. */
/***********************************************************************/
EXTDEF::EXTDEF(void)
{
Tabname = Tabschema = Username = Password = Tabcat = Tabtyp = NULL;
Colpat = Srcdef = Qchar = Qrystr = Sep = Phpos = NULL;
Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0;
Scrollable = Xsrc = false;
} // end of EXTDEF constructor
/***********************************************************************/
/* DefineAM: define specific AM block values from XDB file. */
/***********************************************************************/
bool EXTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
if (g->Createas) {
strcpy(g->Message,
"Multiple-table UPDATE/DELETE commands are not supported");
return true;
} // endif multi
Desc = NULL;
Tabname = GetStringCatInfo(g, "Name",
(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
Tabname = GetStringCatInfo(g, "Tabname", Tabname);
Tabschema = GetStringCatInfo(g, "Dbname", NULL);
Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
Username = GetStringCatInfo(g, "User", NULL);
Password = GetStringCatInfo(g, "Password", NULL);
if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
Read_Only = true;
Qrystr = GetStringCatInfo(g, "Query_String", "?");
Sep = GetStringCatInfo(g, "Separator", NULL);
//Alias = GetStringCatInfo(g, "Alias", NULL);
Phpos = GetStringCatInfo(g, "Phpos", NULL);
Xsrc = GetBoolCatInfo("Execsrc", FALSE);
Maxerr = GetIntCatInfo("Maxerr", 0);
Maxres = GetIntCatInfo("Maxres", 0);
Quoted = GetIntCatInfo("Quoted", 0);
Options = 0;
Cto = 0;
Qto = 0;
if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt)
Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch
if (Catfunc == FNC_COL)
Colpat = GetStringCatInfo(g, "Colpat", NULL);
if (Catfunc == FNC_TABLE)
Tabtyp = GetStringCatInfo(g, "Tabtype", NULL);
// Memory was Boolean, it is now integer
if (!(Memory = GetIntCatInfo("Memory", 0)))
Memory = GetBoolCatInfo("Memory", false) ? 1 : 0;
Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
/* ---------------------------TDBEXT class --------------------------- */
/***********************************************************************/
/* Implementation of the TDBEXT class. */
/***********************************************************************/
TDBEXT::TDBEXT(EXTDEF *tdp) : TDB(tdp)
{
Qrp = NULL;
if (tdp) {
TableName = tdp->Tabname;
Schema = tdp->Tabschema;
User = tdp->Username;
Pwd = tdp->Password;
Catalog = tdp->Tabcat;
Srcdef = tdp->Srcdef;
Qrystr = tdp->Qrystr;
Sep = tdp->GetSep();
Options = tdp->Options;
Cto = tdp->Cto;
Qto = tdp->Qto;
Quoted = MY_MAX(0, tdp->GetQuoted());
Rows = tdp->GetElemt();
Memory = tdp->Memory;
Scrollable = tdp->Scrollable;
} else {
TableName = NULL;
Schema = NULL;
User = NULL;
Pwd = NULL;
Catalog = NULL;
Srcdef = NULL;
Qrystr = NULL;
Sep = 0;
Options = 0;
Cto = 0;
Qto = 0;
Quoted = 0;
Rows = 0;
Memory = 0;
Scrollable = false;
} // endif tdp
Quote = NULL;
Query = NULL;
Count = NULL;
//Where = NULL;
MulConn = NULL;
DBQ = NULL;
Qrp = NULL;
Fpos = 0;
Curpos = 0;
AftRows = 0;
CurNum = 0;
Rbuf = 0;
BufSize = 0;
Nparm = 0;
Ncol = 0;
Placed = false;
} // end of TDBEXT constructor
TDBEXT::TDBEXT(PTDBEXT tdbp) : TDB(tdbp)
{
Qrp = tdbp->Qrp;
TableName = tdbp->TableName;
Schema = tdbp->Schema;
User = tdbp->User;
Pwd = tdbp->Pwd;
Catalog = tdbp->Catalog;
Srcdef = tdbp->Srcdef;
Qrystr = tdbp->Qrystr;
Sep = tdbp->Sep;
Options = tdbp->Options;
Cto = tdbp->Cto;
Qto = tdbp->Qto;
Quoted = tdbp->Quoted;
Rows = tdbp->Rows;
Memory = tdbp->Memory;
Scrollable = tdbp->Scrollable;
Quote = tdbp->Quote;
Query = tdbp->Query;
Count = tdbp->Count;
//Where = tdbp->Where;
MulConn = tdbp->MulConn;
DBQ = tdbp->DBQ;
Fpos = 0;
Curpos = 0;
AftRows = 0;
CurNum = 0;
Rbuf = 0;
BufSize = tdbp->BufSize;
Nparm = tdbp->Nparm;
Ncol = tdbp->Ncol;
Placed = false;
} // end of TDBEXT copy constructor
/******************************************************************/
/* Convert an UTF-8 string to latin characters. */
/******************************************************************/
int TDBEXT::Decode(PCSZ txt, char *buf, size_t n)
{
uint dummy_errors;
uint32 len = copy_and_convert(buf, n, &my_charset_latin1,
txt, strlen(txt),
&my_charset_utf8_general_ci,
&dummy_errors);
buf[len] = '\0';
return 0;
} // end of Decode
/***********************************************************************/
/* MakeSrcdef: make the SQL statement from SRDEF option. */
/***********************************************************************/
bool TDBEXT::MakeSrcdef(PGLOBAL g)
{
char *catp = strstr(Srcdef, "%s");
if (catp) {
char *fil1 = 0, *fil2;
PCSZ ph = ((EXTDEF*)To_Def)->Phpos;
if (!ph)
ph = (strstr(catp + 2, "%s")) ? "WH" : "W";
if (stricmp(ph, "H")) {
fil1 = (To_CondFil && *To_CondFil->Body)
? To_CondFil->Body : PlugDup(g, "1=1");
} // endif ph
if (stricmp(ph, "W")) {
fil2 = (To_CondFil && To_CondFil->Having && *To_CondFil->Having)
? To_CondFil->Having : PlugDup(g, "1=1");
} // endif ph
if (!stricmp(ph, "W")) {
Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1));
Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1));
} else if (!stricmp(ph, "WH")) {
Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1, fil2));
} else if (!stricmp(ph, "H")) {
Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil2));
Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2));
} else if (!stricmp(ph, "HW")) {
Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2, fil1));
} else {
strcpy(g->Message, "MakeSQL: Wrong place holders specification");
return true;
} // endif's ph
} else
Query = new(g)STRING(g, 0, Srcdef);
return false;
} // end of MakeSrcdef
/***********************************************************************/
/* MakeSQL: make the SQL statement use with remote connection. */
/* TODO: when implementing remote filtering, column only used in */
/* local filter should be removed from column list. */
/***********************************************************************/
bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt)
{
PCSZ schmp = NULL;
char *catp = NULL, buf[NAM_LEN * 3];
int len;
bool first = true;
PTABLE tablep = To_Table;
PCOL colp;
if (Srcdef)
return MakeSrcdef(g);
// Allocate the string used to contain the Query
Query = new(g)STRING(g, 1023, "SELECT ");
if (!cnt) {
if (Columns) {
// Normal SQL statement to retrieve results
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial()) {
if (!first)
Query->Append(", ");
else
first = false;
// Column name can be encoded in UTF-8
Decode(colp->GetName(), buf, sizeof(buf));
if (Quote) {
// Put column name between identifier quotes in case in contains blanks
Query->Append(Quote);
Query->Append(buf);
Query->Append(Quote);
} else
Query->Append(buf);
((PEXTCOL)colp)->SetRank(++Ncol);
} // endif colp
} else
// !Columns can occur for queries such that sql count(*) from...
// for which we will count the rows from sql * from...
Query->Append('*');
} else
// SQL statement used to retrieve the size of the result
Query->Append("count(*)");
Query->Append(" FROM ");
if (Catalog && *Catalog)
catp = Catalog;
//if (tablep->GetSchema())
// schmp = (char*)tablep->GetSchema();
//else
if (Schema && *Schema)
schmp = Schema;
if (catp) {
Query->Append(catp);
if (schmp) {
Query->Append('.');
Query->Append(schmp);
} // endif schmp
Query->Append('.');
} else if (schmp) {
Query->Append(schmp);
Query->Append('.');
} // endif schmp
// Table name can be encoded in UTF-8
Decode(TableName, buf, sizeof(buf));
if (Quote) {
// Put table name between identifier quotes in case in contains blanks
Query->Append(Quote);
Query->Append(buf);
Query->Append(Quote);
} else
Query->Append(buf);
len = Query->GetLength();
if (To_CondFil) {
if (Mode == MODE_READ) {
Query->Append(" WHERE ");
Query->Append(To_CondFil->Body);
len = Query->GetLength() + 1;
} else
len += (strlen(To_CondFil->Body) + 256);
} else
len += ((Mode == MODE_READX) ? 256 : 1);
if (Query->IsTruncated()) {
strcpy(g->Message, "MakeSQL: Out of memory");
return true;
} else
Query->Resize(len);
if (trace(33))
htrc("Query=%s\n", Query->GetStr());
return false;
} // end of MakeSQL
/***********************************************************************/
/* Remove the NAME_CONST functions that are added by procedures. */
/***********************************************************************/
void TDBEXT::RemoveConst(PGLOBAL g, char *stmt)
{
char *p, *p2;
char val[1025], nval[1025];
int n, nc;
while ((p = strstr(stmt, "NAME_CONST")))
if ((n = sscanf(p, "%*[^,],%1024[^)])%n", val, &nc))) {
if (trace(33))
htrc("p=%s\nn=%d val=%s nc=%d\n", p, n, val, nc);
*p = 0;
if ((p2 = strstr(val, "'"))) {
if ((n = sscanf(p2, "%*['\\]%1024[^'\\]", nval))) {
if (trace(33))
htrc("p2=%s\nn=%d nval=%s\n", p2, n, nval);
strcat(strcat(strcat(strcat(stmt, "'"), nval), "'"), p + nc);
} else
break;
} else
strcat(strcat(strcat(strcat(stmt, "("), val), ")"), p + nc);
if (trace(33))
htrc("stmt=%s\n", stmt);
} else
break;
return;
} // end of RemoveConst
/***********************************************************************/
/* MakeCommand: make the Update or Delete statement to send to the */
/* MySQL server. Limited to remote values and filtering. */
/***********************************************************************/
bool TDBEXT::MakeCommand(PGLOBAL g)
{
PCSZ schmp = NULL;
char *p, *stmt, name[132], *body = NULL;
char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
bool qtd = Quoted > 0;
char q = qtd ? *Quote : ' ';
int i = 0, k = 0;
// Make a lower case copy of the originale query and change
// back ticks to the data source identifier quoting character
do {
qrystr[i] = (Qrystr[i] == '`') ? q : tolower(Qrystr[i]);
} while (Qrystr[i++]);
if (To_CondFil && (p = strstr(qrystr, " where "))) {
p[7] = 0; // Remove where clause
Qrystr[(p - qrystr) + 7] = 0;
body = To_CondFil->Body;
stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr)
+ strlen(body) + 64);
} else
stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
// Check whether the table name is equal to a keyword
// If so, it must be quoted in the original query
strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
if (strstr(" update delete low_priority ignore quick from ", name)) {
if (Quote) {
strlwr(strcat(strcat(strcpy(name, Quote), Name), Quote));
k += 2;
} else {
strcpy(g->Message, "Quoted must be specified");
return true;
} // endif Quote
} else
strlwr(strcpy(name, Name)); // Not a keyword
if ((p = strstr(qrystr, name))) {
for (i = 0; i < p - qrystr; i++)
stmt[i] = (Qrystr[i] == '`') ? q : Qrystr[i];
stmt[i] = 0;
k += i + (int)strlen(Name);
if (Schema && *Schema)
schmp = Schema;
if (qtd && *(p - 1) == ' ') {
if (schmp)
strcat(strcat(stmt, schmp), ".");
strcat(strcat(strcat(stmt, Quote), TableName), Quote);
} else {
if (schmp) {
if (qtd && *(p - 1) != ' ') {
stmt[i - 1] = 0;
strcat(strcat(strcat(stmt, schmp), "."), Quote);
} else
strcat(strcat(stmt, schmp), ".");
} // endif schmp
strcat(stmt, TableName);
} // endif's
i = (int)strlen(stmt);
do {
stmt[i++] = (Qrystr[k] == '`') ? q : Qrystr[k];
} while (Qrystr[k++]);
RemoveConst(g, stmt);
if (body)
strcat(stmt, body);
} else {
sprintf(g->Message, "Cannot use this %s command",
(Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
return true;
} // endif p
if (trace(33))
htrc("Command=%s\n", stmt);
Query = new(g)STRING(g, 0, stmt);
return (!Query->GetSize());
} // end of MakeCommand
/***********************************************************************/
/* GetRecpos: return the position of last read record. */
/***********************************************************************/
int TDBEXT::GetRecpos(void)
{
return Fpos;
} // end of GetRecpos
/***********************************************************************/
/* ODBC GetMaxSize: returns table size estimate in number of lines. */
/***********************************************************************/
int TDBEXT::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
if (Mode == MODE_DELETE)
// Return 0 in mode DELETE in case of delete all.
MaxSize = 0;
else if (!Cardinality(NULL))
MaxSize = 10; // To make MySQL happy
else if ((MaxSize = Cardinality(g)) < 0)
MaxSize = 12; // So we can see an error occurred
} // endif MaxSize
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* Return max size value. */
/***********************************************************************/
int TDBEXT::GetProgMax(PGLOBAL g)
{
return GetMaxSize(g);
} // end of GetProgMax
/* ---------------------------EXTCOL class --------------------------- */
/***********************************************************************/
/* EXTCOL public constructor. */
/***********************************************************************/
EXTCOL::EXTCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
: COLBLK(cdp, tdbp, i)
{
if (cprec) {
Next = cprec->GetNext();
cprec->SetNext(this);
} else {
Next = tdbp->GetColumns();
tdbp->SetColumns(this);
} // endif cprec
if (trace(1))
htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
// Set additional remote access method information for column.
Crp = NULL;
Long = Precision;
To_Val = NULL;
Bufp = NULL;
Blkp = NULL;
Rank = 0; // Not known yet
} // end of JDBCCOL constructor
/***********************************************************************/
/* EXTCOL private constructor. */
/***********************************************************************/
EXTCOL::EXTCOL(void) : COLBLK()
{
Crp = NULL;
Buf_Type = TYPE_INT; // This is a count(*) column
// Set additional Dos access method information for column.
Long = sizeof(int);
To_Val = NULL;
Bufp = NULL;
Blkp = NULL;
Rank = 1;
} // end of EXTCOL constructor
/***********************************************************************/
/* EXTCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
EXTCOL::EXTCOL(PEXTCOL col1, PTDB tdbp) : COLBLK(col1, tdbp)
{
Crp = col1->Crp;
Long = col1->Long;
To_Val = col1->To_Val;
Bufp = col1->Bufp;
Blkp = col1->Blkp;
Rank = col1->Rank;
} // end of JDBCCOL copy constructor
/***********************************************************************/
/* SetBuffer: prepare a column block for write operation. */
/***********************************************************************/
bool EXTCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
{
if (!(To_Val = value)) {
sprintf(g->Message, MSG(VALUE_ERROR), Name);
return true;
} else if (Buf_Type == value->GetType()) {
// Values are of the (good) column type
if (Buf_Type == TYPE_DATE) {
// If any of the date values is formatted
// output format must be set for the receiving table
if (GetDomain() || ((DTVAL *)value)->IsFormatted())
goto newval; // This will make a new value;
} else if (Buf_Type == TYPE_DOUBLE)
// Float values must be written with the correct (column) precision
// Note: maybe this should be forced by ShowValue instead of this ?
value->SetPrec(GetScale());
Value = value; // Directly access the external value
} else {
// Values are not of the (good) column type
if (check) {
sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
GetTypeName(Buf_Type), GetTypeName(value->GetType()));
return true;
} // endif check
newval:
if (InitValue(g)) // Allocate the matching value block
return true;
} // endif's Value, Buf_Type
// Because Colblk's have been made from a copy of the original TDB in
// case of Update, we must reset them to point to the original one.
if (To_Tdb->GetOrig())
To_Tdb = (PTDB)To_Tdb->GetOrig();
// Set the Column
Status = (ok) ? BUF_EMPTY : BUF_NO;
return false;
} // end of SetBuffer