mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 20:12:31 +01:00
19af1890b5
This commit replaces sprintf(buf, ...) with snprintf(buf, sizeof(buf), ...), specifically in the "easy" cases where buf is allocated with a size known at compile time. The changes make sure we are not write outside array/string bounds which will lead to undefined behaviour. In case the code is trying to write outside bounds - safe version of functions simply cut the string messages so we process this gracefully. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc. bsonudf.cpp warnings cleanup by Daniel Black Reviewer: Daniel Black
451 lines
14 KiB
C++
451 lines
14 KiB
C++
/************** mongo C++ Program Source Code File (.CPP) **************/
|
|
/* PROGRAM NAME: mongo Version 1.1 */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2021 */
|
|
/* These programs are the MGODEF class execution routines. */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant sections of the MariaDB header file. */
|
|
/***********************************************************************/
|
|
#include <my_global.h>
|
|
|
|
/***********************************************************************/
|
|
/* Include application header files: */
|
|
/* global.h is header containing all global declarations. */
|
|
/* plgdbsem.h is header containing the DB application declarations. */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "xtable.h"
|
|
#include "tabext.h"
|
|
#include "filter.h"
|
|
#if defined(CMGO_SUPPORT)
|
|
#include "tabcmg.h"
|
|
#endif // CMGO_SUPPORT
|
|
#if defined(JAVA_SUPPORT)
|
|
#include "tabjmg.h"
|
|
#endif // JAVA_SUPPORT
|
|
#include "resource.h"
|
|
|
|
/***********************************************************************/
|
|
/* This should be an option. */
|
|
/***********************************************************************/
|
|
#define MAXCOL 200 /* Default max column nb in result */
|
|
#define TYPE_UNKNOWN 12 /* Must be greater than other types */
|
|
|
|
bool MakeSelector(PGLOBAL g, PFIL fp, PSTRG s);
|
|
bool IsNum(PSZ s);
|
|
int GetDefaultDepth(void);
|
|
bool JsonAllPath(void);
|
|
|
|
/***********************************************************************/
|
|
/* Make selector json representation for Mongo tables. */
|
|
/***********************************************************************/
|
|
bool MakeSelector(PGLOBAL g, PFIL fp, PSTRG s)
|
|
{
|
|
OPVAL opc = fp->GetOpc();
|
|
|
|
s->Append('{');
|
|
|
|
if (opc == OP_AND || opc == OP_OR) {
|
|
if (fp->GetArgType(0) != TYPE_FILTER || fp->GetArgType(1) != TYPE_FILTER)
|
|
return true;
|
|
|
|
s->Append("\"$");
|
|
s->Append(opc == OP_AND ? "and" : "or");
|
|
s->Append("\":[");
|
|
|
|
if (MakeSelector(g, (PFIL)fp->Arg(0), s))
|
|
return true;
|
|
|
|
s->Append(',');
|
|
|
|
if (MakeSelector(g, (PFIL)fp->Arg(1), s))
|
|
return true;
|
|
|
|
s->Append(']');
|
|
} else {
|
|
if (fp->GetArgType(0) != TYPE_COLBLK)
|
|
return true;
|
|
|
|
s->Append('"');
|
|
s->Append(((PCOL)fp->Arg(0))->GetJpath(g, false));
|
|
s->Append("\":{\"$");
|
|
|
|
switch (opc) {
|
|
case OP_EQ:
|
|
s->Append("eq");
|
|
break;
|
|
case OP_NE:
|
|
s->Append("ne");
|
|
break;
|
|
case OP_GT:
|
|
s->Append("gt");
|
|
break;
|
|
case OP_GE:
|
|
s->Append("gte");
|
|
break;
|
|
case OP_LT:
|
|
s->Append("lt");
|
|
break;
|
|
case OP_LE:
|
|
s->Append("lte");
|
|
break;
|
|
case OP_NULL:
|
|
case OP_LIKE:
|
|
case OP_EXIST:
|
|
default:
|
|
return true;
|
|
} // endswitch Opc
|
|
|
|
s->Append("\":");
|
|
|
|
if (fp->GetArgType(1) == TYPE_COLBLK) {
|
|
s->Append("\"$");
|
|
s->Append(((PEXTCOL)fp->Arg(1))->GetJpath(g, false));
|
|
s->Append('"');
|
|
} else {
|
|
char buf[501];
|
|
|
|
fp->Arg(1)->Prints(g, buf, 500);
|
|
s->Append(buf);
|
|
} // endif Type
|
|
|
|
s->Append('}');
|
|
} // endif opc
|
|
|
|
s->Append('}');
|
|
return false;
|
|
} // end of MakeSelector
|
|
|
|
/***********************************************************************/
|
|
/* MGOColumns: construct the result blocks containing the description */
|
|
/* of all the columns of a document contained inside MongoDB. */
|
|
/***********************************************************************/
|
|
PQRYRES MGOColumns(PGLOBAL g, PCSZ db, PCSZ uri, PTOS topt, bool info)
|
|
{
|
|
static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
|
|
TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
|
|
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
|
|
FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
|
|
unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
|
|
int ncol = sizeof(buftyp) / sizeof(int);
|
|
int i, n = 0;
|
|
PCSZ drv;
|
|
PBCOL bcp;
|
|
MGODISC *cmgd = NULL;
|
|
PQRYRES qrp;
|
|
PCOLRES crp;
|
|
|
|
if (info) {
|
|
length[0] = 128;
|
|
length[7] = 256;
|
|
goto skipit;
|
|
} // endif info
|
|
|
|
/*********************************************************************/
|
|
/* Open MongoDB. */
|
|
/*********************************************************************/
|
|
drv = GetStringTableOption(g, topt, "Driver", NULL);
|
|
|
|
if (drv && toupper(*drv) == 'C') {
|
|
#if defined(CMGO_SUPPORT)
|
|
cmgd = new(g) CMGDISC(g, (int*)length);
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "C");
|
|
goto err;
|
|
#endif
|
|
} else if (drv && toupper(*drv) == 'J') {
|
|
#if defined(JAVA_SUPPORT)
|
|
cmgd = new(g) JMGDISC(g, (int*)length);
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "Java");
|
|
goto err;
|
|
#endif
|
|
} else { // Driver not specified
|
|
#if defined(CMGO_SUPPORT)
|
|
cmgd = new(g) CMGDISC(g, (int*)length);
|
|
#else
|
|
cmgd = new(g) JMGDISC(g, (int*)length);
|
|
#endif
|
|
} // endif drv
|
|
|
|
if ((n = cmgd->GetColumns(g, db, uri, topt)) < 0)
|
|
goto err;
|
|
|
|
skipit:
|
|
if (trace(1))
|
|
htrc("MGOColumns: n=%d len=%d\n", n, length[0]);
|
|
|
|
/*********************************************************************/
|
|
/* Allocate the structures used to refer to the result set. */
|
|
/*********************************************************************/
|
|
qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
|
|
buftyp, fldtyp, length, false, false);
|
|
|
|
crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
|
|
crp->Name = "Nullable";
|
|
crp->Next->Name = "Bpath";
|
|
|
|
if (info || !qrp)
|
|
return qrp;
|
|
|
|
qrp->Nblin = n;
|
|
|
|
/*********************************************************************/
|
|
/* Now get the results into blocks. */
|
|
/*********************************************************************/
|
|
for (i = 0, bcp = cmgd->fbcp; bcp; i++, bcp = bcp->Next) {
|
|
if (bcp->Type == TYPE_UNKNOWN) // Void column
|
|
bcp->Type = TYPE_STRING;
|
|
|
|
crp = qrp->Colresp; // Column Name
|
|
crp->Kdata->SetValue(bcp->Name, i);
|
|
crp = crp->Next; // Data Type
|
|
crp->Kdata->SetValue(bcp->Type, i);
|
|
crp = crp->Next; // Type Name
|
|
crp->Kdata->SetValue(GetTypeName(bcp->Type), i);
|
|
crp = crp->Next; // Precision
|
|
crp->Kdata->SetValue(bcp->Len, i);
|
|
crp = crp->Next; // Length
|
|
crp->Kdata->SetValue(bcp->Len, i);
|
|
crp = crp->Next; // Scale (precision)
|
|
crp->Kdata->SetValue(bcp->Scale, i);
|
|
crp = crp->Next; // Nullable
|
|
crp->Kdata->SetValue(bcp->Cbn ? 1 : 0, i);
|
|
crp = crp->Next; // Field format
|
|
|
|
if (crp->Kdata)
|
|
crp->Kdata->SetValue(bcp->Fmt, i);
|
|
|
|
} // endfor i
|
|
|
|
/*********************************************************************/
|
|
/* Return the result pointer. */
|
|
/*********************************************************************/
|
|
return qrp;
|
|
|
|
err:
|
|
if (cmgd && cmgd->tmgp)
|
|
cmgd->tmgp->CloseDB(g);
|
|
|
|
return NULL;
|
|
} // end of MGOColumns
|
|
|
|
/***********************************************************************/
|
|
/* Class used to get the columns of a mongo collection. */
|
|
/***********************************************************************/
|
|
MGODISC::MGODISC(PGLOBAL g, int *lg) {
|
|
length = lg;
|
|
fbcp = NULL;
|
|
pbcp = NULL;
|
|
tmgp = NULL;
|
|
drv = NULL;
|
|
i = ncol = lvl = 0;
|
|
all = false;
|
|
} // end of MGODISC constructor
|
|
|
|
/***********************************************************************/
|
|
/* Class used to get the columns of a mongo collection. */
|
|
/***********************************************************************/
|
|
int MGODISC::GetColumns(PGLOBAL g, PCSZ db, PCSZ uri, PTOS topt)
|
|
{
|
|
PMGODEF tdp;
|
|
|
|
lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
|
|
lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
|
|
all = GetBooleanTableOption(g, topt, "Fullarray", false);
|
|
|
|
/*********************************************************************/
|
|
/* Open the MongoDB collection. */
|
|
/*********************************************************************/
|
|
tdp = new(g) MGODEF;
|
|
tdp->Uri = (uri && *uri) ? uri : "mongodb://localhost:27017";
|
|
tdp->Driver = drv;
|
|
tdp->Tabname = GetStringTableOption(g, topt, "Name", NULL);
|
|
tdp->Tabname = GetStringTableOption(g, topt, "Tabname", tdp->Tabname);
|
|
tdp->Tabschema = GetStringTableOption(g, topt, "Dbname", db);
|
|
tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0;
|
|
tdp->Colist = GetStringTableOption(g, topt, "Colist", "all");
|
|
tdp->Filter = GetStringTableOption(g, topt, "Filter", NULL);
|
|
tdp->Pipe = GetBooleanTableOption(g, topt, "Pipeline", false);
|
|
tdp->Version = GetIntegerTableOption(g, topt, "Version", 3);
|
|
tdp->Wrapname = (PSZ)GetStringTableOption(g, topt, "Wrapper",
|
|
(tdp->Version == 2) ? "Mongo2Interface" : "Mongo3Interface");
|
|
|
|
if (trace(1))
|
|
htrc("Uri %s coll=%s db=%s colist=%s filter=%s lvl=%d\n",
|
|
tdp->Uri, tdp->Tabname, tdp->Tabschema, tdp->Colist, tdp->Filter, lvl);
|
|
|
|
tmgp = tdp->GetTable(g, MODE_READ);
|
|
tmgp->SetMode(MODE_READ);
|
|
|
|
if (tmgp->OpenDB(g))
|
|
return -1;
|
|
|
|
bcol.Next = NULL;
|
|
bcol.Name = bcol.Fmt = NULL;
|
|
bcol.Type = TYPE_UNKNOWN;
|
|
bcol.Len = bcol.Scale = 0;
|
|
bcol.Found = true;
|
|
bcol.Cbn = false;
|
|
|
|
if (Init(g))
|
|
return -1;
|
|
|
|
/*********************************************************************/
|
|
/* Analyse the BSON tree and define columns. */
|
|
/*********************************************************************/
|
|
for (i = 1; ; i++) {
|
|
switch (tmgp->ReadDB(g)) {
|
|
case RC_EF:
|
|
return ncol;
|
|
case RC_FX:
|
|
return -1;
|
|
default:
|
|
GetDoc();
|
|
} // endswitch ReadDB
|
|
|
|
if (Find(g))
|
|
return -1;
|
|
|
|
// Missing columns can be null
|
|
for (bcp = fbcp; bcp; bcp = bcp->Next) {
|
|
bcp->Cbn |= !bcp->Found;
|
|
bcp->Found = false;
|
|
} // endfor bcp
|
|
|
|
} // endfor i
|
|
|
|
return ncol;
|
|
} // end of GetColumns
|
|
|
|
/***********************************************************************/
|
|
/* Add a new column in the column list. */
|
|
/***********************************************************************/
|
|
void MGODISC::AddColumn(PGLOBAL g, PCSZ colname, PCSZ fmt, int k)
|
|
{
|
|
// Check whether this column was already found
|
|
for (bcp = fbcp; bcp; bcp = bcp->Next)
|
|
if (!strcmp(colname, bcp->Name))
|
|
break;
|
|
|
|
if (bcp) {
|
|
if (bcp->Type != bcol.Type)
|
|
bcp->Type = TYPE_STRING;
|
|
|
|
if (k && *fmt && (!bcp->Fmt || strlen(bcp->Fmt) < strlen(fmt))) {
|
|
bcp->Fmt = PlugDup(g, fmt);
|
|
length[7] = MY_MAX(length[7], (signed)strlen(fmt));
|
|
} // endif *fmt
|
|
|
|
bcp->Len = MY_MAX(bcp->Len, bcol.Len);
|
|
bcp->Scale = MY_MAX(bcp->Scale, bcol.Scale);
|
|
bcp->Cbn |= bcol.Cbn;
|
|
bcp->Found = true;
|
|
} else {
|
|
// New column
|
|
bcp = (PBCOL)PlugSubAlloc(g, NULL, sizeof(BCOL));
|
|
*bcp = bcol;
|
|
bcp->Cbn |= (i > 1);
|
|
bcp->Name = PlugDup(g, colname);
|
|
length[0] = MY_MAX(length[0], (signed)strlen(colname));
|
|
|
|
if (k || JsonAllPath()) {
|
|
bcp->Fmt = PlugDup(g, fmt);
|
|
length[7] = MY_MAX(length[7], (signed)strlen(fmt));
|
|
} else
|
|
bcp->Fmt = NULL;
|
|
|
|
if (pbcp) {
|
|
bcp->Next = pbcp->Next;
|
|
pbcp->Next = bcp;
|
|
} else
|
|
fbcp = bcp;
|
|
|
|
ncol++;
|
|
} // endif jcp
|
|
|
|
pbcp = bcp;
|
|
} // end of AddColumn
|
|
|
|
/* -------------------------- Class MGODEF --------------------------- */
|
|
|
|
MGODEF::MGODEF(void)
|
|
{
|
|
Driver = NULL;
|
|
Uri = NULL;
|
|
Colist = NULL;
|
|
Filter = NULL;
|
|
Base = 0;
|
|
Version = 0;
|
|
Pipe = false;
|
|
} // end of MGODEF constructor
|
|
|
|
/***********************************************************************/
|
|
/* DefineAM: define specific AM block values. */
|
|
/***********************************************************************/
|
|
bool MGODEF::DefineAM(PGLOBAL g, LPCSTR, int poff)
|
|
{
|
|
if (EXTDEF::DefineAM(g, "MGO", poff))
|
|
return true;
|
|
else if (!Tabschema)
|
|
Tabschema = GetStringCatInfo(g, "Dbname", "*");
|
|
|
|
Driver = GetStringCatInfo(g, "Driver", NULL);
|
|
Uri = GetStringCatInfo(g, "Connect", "mongodb://localhost:27017");
|
|
Colist = GetStringCatInfo(g, "Colist", NULL);
|
|
Filter = GetStringCatInfo(g, "Filter", NULL);
|
|
Strfy = GetStringCatInfo(g, "Stringify", NULL);
|
|
Base = GetIntCatInfo("Base", 0) ? 1 : 0;
|
|
Version = GetIntCatInfo("Version", 3);
|
|
|
|
if (Version == 2)
|
|
Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo2Interface");
|
|
else
|
|
Wrapname = GetStringCatInfo(g, "Wrapper", "Mongo3Interface");
|
|
|
|
Pipe = GetBoolCatInfo("Pipeline", false);
|
|
return false;
|
|
} // end of DefineAM
|
|
|
|
/***********************************************************************/
|
|
/* GetTable: makes a new Table Description Block. */
|
|
/***********************************************************************/
|
|
PTDB MGODEF::GetTable(PGLOBAL g, MODE m)
|
|
{
|
|
if (Driver && toupper(*Driver) == 'C') {
|
|
#if defined(CMGO_SUPPORT)
|
|
if (Catfunc == FNC_COL)
|
|
return new(g) TDBGOL(this);
|
|
else
|
|
return new(g) TDBCMG(this);
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "C");
|
|
return NULL;
|
|
#endif
|
|
} else if (Driver && toupper(*Driver) == 'J') {
|
|
#if defined(JAVA_SUPPORT)
|
|
if (Catfunc == FNC_COL)
|
|
return new(g) TDBJGL(this);
|
|
else
|
|
return new(g) TDBJMG(this);
|
|
#else
|
|
snprintf(g->Message, sizeof(g->Message), "Mongo %s Driver not available", "Java");
|
|
return NULL;
|
|
#endif
|
|
} else { // Driver not specified
|
|
#if defined(CMGO_SUPPORT)
|
|
if (Catfunc == FNC_COL)
|
|
return new(g) TDBGOL(this);
|
|
else
|
|
return new(g) TDBCMG(this);
|
|
#else
|
|
if (Catfunc == FNC_COL)
|
|
return new(g) TDBJGL(this);
|
|
else
|
|
return new(g) TDBJMG(this);
|
|
#endif
|
|
} // endif Driver
|
|
|
|
} // end of GetTable
|