1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

- Fix Catalog JSON table crash when no Jpath

- Added JSON OBJECT specification for pretty != 2.
- Fix NULL values not recognized for nullable JSON columns
- Issue an error message when a JSON table is created without specifying LRECL if PRETTY != 2.
- Make JSONColumns use a TDBJSON class.
- Make JSON table using MAPFAM
modified:
  filamap.h
  filamtxt.h
  ha_connect.cc
  json.result
  tabjson.cpp
  tabjson.h
  table.cpp

- Implementing Discovery for the XML table type.
modified:
  domdoc.cpp
  domdoc.h
  ha_connect.cc
  libdoc.cpp
  plgxml.cpp
  plgxml.h
  reldef.cpp
  reldef.h
  tabxml.cpp
  tabxml.h

- Providing an error message when creating an ODBC table via discovery returns
  columns of more than one table.
modified:
  ha_connect.cc

- TableOptionStruct declaration moved from ha_connect.h to mycat.h
  To make it easier to use by other classes.
modified:
  ha_connect.cc
  ha_connect.h
  mycat.cc
  mycat.h
  reldef.cpp
  tabmysql.cpp
  taboccur.cpp
  tabpivot.cpp
  tabtbl.cpp
  tabutil.cpp
  tabxcl.cpp
This commit is contained in:
Olivier Bertrand
2015-04-17 20:05:41 +02:00
parent 05b30fbcc3
commit eae8318b19
25 changed files with 896 additions and 395 deletions

View File

@@ -19,7 +19,6 @@
#include "global.h"
#include "plgdbsem.h"
//#include "xtable.h"
//#include "mycat.h" // for FNC_COL
#include "maputil.h"
#include "filamtxt.h"
#include "tabdos.h"
@@ -32,7 +31,7 @@
#include "tabmul.h"
#include "checklvl.h"
#include "resource.h"
#include "mycat.h"
#include "mycat.h" // for FNC_COL
/***********************************************************************/
/* This should be an option. */
@@ -45,140 +44,6 @@
/***********************************************************************/
USETEMP UseTemp(void);
/***********************************************************************/
/* Make the document tree from a file. */
/***********************************************************************/
PJSON MakeJsonTree(PGLOBAL g, char *fn, char *objn, int pty,
PJAR& doc, DWORD& drc)
{
char *p, *memory, *objpath, *key;
int len, i = 0;
HANDLE hFile;
MEMMAP mm;
PJSON jsp, top;
PJOB objp = NULL;
PJAR arp = NULL;
PJVAL val = NULL;
/*********************************************************************/
/* Create the mapping file object. */
/*********************************************************************/
hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);
if (hFile == INVALID_HANDLE_VALUE) {
drc = GetLastError();
if (!*g->Message)
sprintf(g->Message, MSG(OPEN_MODE_ERROR), "map", (int)drc, fn);
return NULL;
} // endif hFile
/*********************************************************************/
/* Get the file size (assuming file is smaller than 4 GB) */
/*********************************************************************/
len = mm.lenL;
memory = (char *)mm.memory;
if (!len) { // Empty file
CloseFileHandle(hFile);
CloseMemMap(memory, len);
drc = ENOENT;
return NULL;
} else if (!memory) {
sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, drc);
CloseFileHandle(hFile);
return NULL;
} // endif Memory
CloseFileHandle(hFile); // Not used anymore
/*********************************************************************/
/* Parse the json file and allocate its tree structure. */
/*********************************************************************/
g->Message[0] = 0;
jsp = top = ParseJson(g, memory, len, pty);
CloseMemMap(memory, len);
drc = EBADF;
if (!jsp && g->Message[0])
return NULL;
objpath = PlugDup(g, objn); // NULL if !objn
/*********************************************************************/
/* Find the table in the tree structure. */
/*********************************************************************/
for (doc = NULL; jsp && objpath; objpath = p) {
if ((p = strchr(objpath, ':')))
*p++ = 0;
if (*objpath != '[') { // objpass is a key
if (jsp->GetType() != TYPE_JOB) {
strcpy(g->Message, "Table path does no match json file");
return NULL;
} // endif Type
key = objpath;
objp = jsp->GetObject();
arp = NULL;
val = objp->GetValue(key);
if (!val || !(jsp = val->GetJson())) {
sprintf(g->Message, "Cannot find object key %s", key);
return NULL;
} // endif val
} else if (objpath[strlen(objpath)-1] == ']') {
if (jsp->GetType() != TYPE_JAR) {
strcpy(g->Message, "Table path does no match json file");
return NULL;
} // endif Type
arp = jsp->GetArray();
objp = NULL;
i = atoi(objpath+1) - 1;
val = arp->GetValue(i);
if (!val) {
sprintf(g->Message, "Cannot find array value %d", i);
return NULL;
} // endif val
} else {
sprintf(g->Message, "Invalid Table path %s", objn);
return NULL;
} // endif objpath
jsp = val->GetJson();
} // endfor objpath
if (jsp && jsp->GetType() == TYPE_JAR)
doc = jsp->GetArray();
else {
// The table is void or is just one object or one value
doc = new(g) JARRAY;
if (val) {
doc->AddValue(g, val);
doc->InitArray(g);
} else if (jsp) {
doc->AddValue(g, new(g) JVALUE(jsp));
doc->InitArray(g);
} // endif val
if (objp)
objp->SetValue(g, new(g) JVALUE(doc), key);
else if (arp)
arp->SetValue(g, new(g) JVALUE(doc), i);
else
top = doc;
} // endif jsp
return top;
} // end of MakeJsonTree
typedef struct _jncol {
struct _jncol *Next;
char *Name;
@@ -202,11 +67,9 @@ PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
char filename[_MAX_PATH], colname[65], fmt[129], *buf;
char filename[_MAX_PATH], colname[65], fmt[129];
int i, j, n = 0;
int ncol = sizeof(buftyp) / sizeof(int);
FILE *infile;
DWORD drc;
PVAL valp;
JCOL jcol;
PJCL jcp, fjcp = NULL, pjcp = NULL;
@@ -214,7 +77,9 @@ PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
PJSON jsp;
PJVAL jvp;
PJOB row;
PJAR doc;
PJDEF tdp;
TDBJSN *tjnp;
PJTDB tjsp;
PQRYRES qrp;
PCOLRES crp;
@@ -235,43 +100,49 @@ PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
strcpy(g->Message, MSG(MISSING_FNAME));
return NULL;
} else
PlugSetPath(filename, fn, dp);
PlugSetPath(filename, fn, dp);
tdp = new(g) JSONDEF;
tdp->Database = dp;
tdp->Fn = filename;
tdp->Objname = objn;
tdp->Pretty = pretty;
if (pretty == 2) {
if (!MakeJsonTree(g, filename, objn, pretty, doc, drc))
tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp));
if (tjsp->MakeDocument(g))
return NULL;
jsp = (doc) ? doc->GetValue(0) : NULL;
jsp = (tjsp->GetDoc()) ? tjsp->GetDoc()->GetValue(0) : NULL;
} else {
if (!lrecl) {
sprintf(g->Message, "LRECL must be specified for pretty=%d", pretty);
return NULL;
} else if (!(buf = (char*)PlugSubAlloc(g, NULL, lrecl))) {
sprintf(g->Message, "Alloc error, lrecl=%d", lrecl);
return NULL;
} // endif buf
} // endif lrecl
if (!(infile = global_fopen(g, MSGID_CANNOT_OPEN, filename, "r")))
tdp->Lrecl = lrecl;
tdp->Ending = CRLF;
tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp));
tjnp->SetMode(MODE_READ);
if (tjnp->OpenDB(g))
return NULL;
// Read first record
for (i = 0; i <= pretty; i++)
if (!fgets(buf, lrecl, infile)) {
if (feof(infile)) {
strcpy(g->Message, "Void json table");
return NULL;
} // endif fgets
switch (tjnp->ReadDB(g)) {
case RC_EF:
strcpy(g->Message, "Void json table");
case RC_FX:
goto err;
default:
jsp = tjnp->GetRow();
} // endswitch ReadDB
sprintf(g->Message, MSG(READ_ERROR), filename, strerror(0));
return NULL;
} // endif fgets
jsp = ParseJson(g, buf, strlen(buf), pretty, NULL);
} // endif pretty
} // endif pretty
if (!(row = (jsp) ? jsp->GetObject() : NULL)) {
strcpy(g->Message, "Can only retrieve columns from object rows");
return NULL;
goto err;
} // endif row
jcol.Next = NULL;
@@ -394,20 +265,18 @@ PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
if (pretty != 2) {
// Read next record
if (!fgets(buf, lrecl, infile)) {
if (!feof(infile)) {
sprintf(g->Message, MSG(READ_ERROR), filename, strerror(0));
return NULL;
} else
switch (tjnp->ReadDB(g)) {
case RC_EF:
jsp = NULL;
} else if (pretty == 1 && strlen(buf) == 1 && *buf == ']') {
jsp = NULL;
} else
jsp = ParseJson(g, buf, strlen(buf), pretty, NULL);
break;
case RC_FX:
goto err;
default:
jsp = tjnp->GetRow();
} // endswitch ReadDB
} else
jsp = doc->GetValue(i);
jsp = tjsp->GetDoc()->GetValue(i);
if (!(row = (jsp) ? jsp->GetObject() : NULL))
break;
@@ -415,7 +284,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
} // endor i
if (pretty != 2)
fclose(infile);
tjnp->CloseDB(g);
skipit:
if (trace)
@@ -471,7 +340,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
err:
if (pretty != 2)
fclose(infile);
tjnp->CloseDB(g);
return NULL;
} // end of JSONColumns
@@ -487,6 +356,7 @@ JSONDEF::JSONDEF(void)
Limit = 1;
Level = 0;
ReadMode = 0;
Strict = false;
} // end of JSONDEF constructor
/***********************************************************************/
@@ -540,7 +410,7 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
// Txfp must be set for TDBDOS
tdbp = new(g) TDBJSN(this, txfp);
} else {
txfp = new(g) DOSFAM(this);
txfp = new(g) MAPFAM(this);
tdbp = new(g) TDBJSON(this, txfp);
} // endif Pretty
@@ -557,30 +427,45 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
/***********************************************************************/
TDBJSN::TDBJSN(PJDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
{
Top = NULL;
Row = NULL;
Val = NULL;
Colp = NULL;
Jmode = tdp->Jmode;
Xcol = tdp->Xcol;
if (tdp) {
Jmode = tdp->Jmode;
Objname = tdp->Objname;
Xcol = tdp->Xcol;
Limit = tdp->Limit;
Pretty = tdp->Pretty;
Strict = tdp->Strict;
} else {
Jmode = MODE_OBJECT;
Objname = NULL;
Xcol = NULL;
Limit = 1;
Pretty = 0;
Strict = false;
} // endif tdp
Fpos = -1;
//Spos = 0;
N = 0;
Limit = tdp->Limit;
NextSame = 0;
SameRow = 0;
Xval = -1;
Pretty = tdp->Pretty;
Strict = tdp->Strict;
Comma = false;
} // end of TDBJSN standard constructor
TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
{
Top = tdbp->Top;
Row = tdbp->Row;
Val = tdbp->Val;
Colp = tdbp->Colp;
Jmode = tdbp->Jmode;
Objname = tdbp->Objname;
Xcol = tdbp->Xcol;
Fpos = tdbp->Fpos;
//Spos = tdbp->Spos;
N = tdbp->N;
Limit = tdbp->Limit;
NextSame = tdbp->NextSame;
@@ -658,6 +543,34 @@ int TDBJSN::GetMaxSize(PGLOBAL g)
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* Find the row in the tree structure. */
/***********************************************************************/
PJSON TDBJSN::FindRow(PGLOBAL g)
{
char *p, *objpath;
PJSON jsp = Row;
PJVAL val = NULL;
for (objpath = PlugDup(g, Objname); jsp && objpath; objpath = p) {
if ((p = strchr(objpath, ':')))
*p++ = 0;
if (*objpath != '[') { // objpass is a key
val = (jsp->GetType() == TYPE_JOB) ?
jsp->GetObject()->GetValue(objpath) : NULL;
} else if (objpath[strlen(objpath)-1] == ']') {
val = (jsp->GetType() == TYPE_JAR) ?
jsp->GetArray()->GetValue(atoi(objpath+1) - 1) : NULL;
} else
val = NULL;
jsp = (val) ? val->GetJson() : NULL;
} // endfor objpath
return jsp;
} // end of FindRow
/***********************************************************************/
/* OpenDB: Data Base open routine for JSN access method. */
/***********************************************************************/
@@ -668,7 +581,6 @@ bool TDBJSN::OpenDB(PGLOBAL g)
/* Table already open replace it at its beginning. */
/*******************************************************************/
Fpos= -1;
// Spos = 0;
NextSame = 0;
SameRow = 0;
} else {
@@ -743,6 +655,7 @@ int TDBJSN::ReadDB(PGLOBAL g)
strlen(To_Line), Pretty, &Comma))) {
rc = (Pretty == 1 && !strcmp(To_Line, "]")) ? RC_EF : RC_FX;
} else {
Row = FindRow(g);
SameRow = 0;
Fpos++;
rc = RC_OK;
@@ -751,20 +664,86 @@ int TDBJSN::ReadDB(PGLOBAL g)
return rc;
} // end of ReadDB
/***********************************************************************/
/* Make the top tree from the object path. */
/***********************************************************************/
int TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp)
{
if (Objname) {
if (!Val) {
// Parse and allocate Objname item(s)
char *p;
char *objpath = PlugDup(g, Objname);
int i;
PJOB objp;
PJAR arp;
PJVAL val = NULL;
Top = NULL;
for (; objpath; objpath = p) {
if ((p = strchr(objpath, ':')))
*p++ = 0;
if (*objpath != '[') {
objp = new(g) JOBJECT;
if (!Top)
Top = objp;
if (val)
val->SetValue(objp);
val = new(g) JVALUE;
objp->SetValue(g, val, objpath);
} else if (objpath[strlen(objpath)-1] == ']') {
arp = new(g) JARRAY;
if (!Top)
Top = arp;
if (val)
val->SetValue(arp);
val = new(g) JVALUE;
i = atoi(objpath+1) - 1;
arp->SetValue(g, val, i);
arp->InitArray(g);
} else {
sprintf(g->Message, "Invalid Table path %s", Objname);
return RC_FX;
} // endif objpath
} // endfor p
Val = val;
} // endif Val
Val->SetValue(jsp);
} else
Top = jsp;
return RC_OK;
} // end of MakeTopTree
/***********************************************************************/
/* PrepareWriting: Prepare the line for WriteDB. */
/***********************************************************************/
bool TDBJSN::PrepareWriting(PGLOBAL g)
{
PSZ s = Serialize(g, Row, NULL, Pretty);
PSZ s;
if (s) {
if (MakeTopTree(g, Row))
return true;
if ((s = Serialize(g, Top, NULL, Pretty))) {
if (Comma)
strcat(s, ",");
if ((signed)strlen(s) > Lrecl) {
sprintf(g->Message, "Line would be truncated (lrecl=%d)", Lrecl);
return true;
strncpy(To_Line, s, Lrecl);
sprintf(g->Message, "Line truncated (lrecl=%d)", Lrecl);
return PushWarning(g, this);
} else
strcpy(To_Line, s);
@@ -1083,6 +1062,10 @@ void JSONCOL::ReadColumn(PGLOBAL g)
if (!Tjp->SameRow || Xnod >= Tjp->SameRow)
Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0));
// Set null when applicable
if (Nullable)
Value->SetNull(Value->IsZero());
} // end of ReadColumn
/***********************************************************************/
@@ -1272,7 +1255,7 @@ PVAL JSONCOL::CalculateArray(PGLOBAL g, PJAR arp, int n)
/***********************************************************************/
PJSON JSONCOL::GetRow(PGLOBAL g)
{
PJVAL val;
PJVAL val = NULL;
PJAR arp;
PJSON nwr, row = Tjp->Row;
@@ -1438,18 +1421,14 @@ void JSONCOL::WriteColumn(PGLOBAL g)
/***********************************************************************/
TDBJSON::TDBJSON(PJDEF tdp, PTXF txfp) : TDBJSN(tdp, txfp)
{
Top = NULL;
Doc = NULL;
Objname = tdp->Objname;
Multiple = tdp->Multiple;
Done = Changed = false;
} // end of TDBJSON standard constructor
TDBJSON::TDBJSON(PJTDB tdbp) : TDBJSN(tdbp)
{
Top = tdbp->Top;
Doc = tdbp->Doc;
Objname = tdbp->Objname;
Multiple = tdbp->Multiple;
Done = tdbp->Done;
Changed = tdbp->Changed;
@@ -1473,64 +1452,17 @@ PTDB TDBJSON::CopyOne(PTABS t)
} // end of CopyOne
/***********************************************************************/
/* Make the document tree from a file. */
/* Make the document tree from the object path. */
/***********************************************************************/
int TDBJSON::MakeNewDoc(PGLOBAL g)
{
// Create a void table that will be populated
Doc = new(g) JARRAY;
if (Objname) {
// Parse and allocate Objname item(s)
char *p;
char *objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname)+1);
int i;
PJOB objp;
PJAR arp;
PJVAL val = NULL;
strcpy(objpath, Objname);
Top = NULL;
for (; objpath; objpath = p) {
if ((p = strchr(objpath, ':')))
*p++ = 0;
if (*objpath != '[') {
objp = new(g) JOBJECT;
if (!Top)
Top = objp;
if (val)
val->SetValue(objp);
val = new(g) JVALUE;
objp->SetValue(g, val, objpath);
} else if (objpath[strlen(objpath)-1] == ']') {
arp = new(g) JARRAY;
if (!Top)
Top = arp;
if (val)
val->SetValue(arp);
val = new(g) JVALUE;
i = atoi(objpath+1) - 1;
arp->SetValue(g, val, i);
arp->InitArray(g);
} else {
sprintf(g->Message, "Invalid Table path %s", Objname);
return RC_FX;
} // endif objpath
} // endfor p
val->SetValue(Doc);
} else
Top = Doc;
if (MakeTopTree(g, Doc))
return RC_FX;
Done = true;
return RC_OK;
} // end of MakeNewDoc
@@ -1539,36 +1471,9 @@ int TDBJSON::MakeNewDoc(PGLOBAL g)
/***********************************************************************/
int TDBJSON::MakeDocument(PGLOBAL g)
{
char filename[_MAX_PATH];
int rc = RC_OK;
DWORD drc;
if (Done)
return RC_OK;
// Now open the JSON file
PlugSetPath(filename, Txfp->To_File, GetPath());
/*********************************************************************/
/* Get top of the parsed tree and the inside table document. */
/*********************************************************************/
if (!(Top = MakeJsonTree(g, filename, Objname, Pretty, Doc, drc)))
rc = (drc == ENOENT && Mode == MODE_INSERT) ? MakeNewDoc(g) : RC_FX;
Done = (rc == RC_OK);
return rc;
} // end of MakeDocument
#if 0
/***********************************************************************/
/* Make the document tree from a file. */
/***********************************************************************/
int TDBJSON::MakeDocument(PGLOBAL g)
{
char *p, *memory, *objpath, *key, filename[_MAX_PATH];
char *p, *memory, *objpath, *key;
int len, i = 0;
HANDLE hFile;
MEMMAP mm;
MODE mode = Mode;
PJSON jsp;
PJOB objp = NULL;
PJAR arp = NULL;
@@ -1576,61 +1481,33 @@ int TDBJSON::MakeDocument(PGLOBAL g)
if (Done)
return RC_OK;
else
Done = true;
// Now open the JSON file
PlugSetPath(filename, Txfp->To_File, GetPath());
/*********************************************************************/
/* Create the mapping file object in mode read. */
/*********************************************************************/
Mode = MODE_READ;
/*********************************************************************/
/* Create the mapping file object. */
/*********************************************************************/
hFile = CreateFileMap(g, filename, &mm, MODE_READ, false);
if (!Txfp->OpenTableFile(g)) {
PFBLOCK fp = Txfp->GetTo_Fb();
if (hFile == INVALID_HANDLE_VALUE) {
DWORD drc = GetLastError();
if (drc != ENOENT || Mode != MODE_INSERT) {
if (!(*g->Message))
sprintf(g->Message, MSG(OPEN_MODE_ERROR),
"map", (int)drc, filename);
return RC_FX;
} else
if (fp) {
len = fp->Length;
memory = fp->Memory;
} else {
Mode = mode; // Restore saved Mode
return MakeNewDoc(g);
} // endif fp
} // endif hFile
/*********************************************************************/
/* Get the file size (assuming file is smaller than 4 GB) */
/*********************************************************************/
len = mm.lenL;
memory = (char *)mm.memory;
if (!len) { // Empty file
CloseFileHandle(hFile);
CloseMemMap(memory, len);
if (Mode == MODE_INSERT)
return MakeNewDoc(g);
} // endif len
if (!memory) {
CloseFileHandle(hFile);
sprintf(g->Message, MSG(MAP_VIEW_ERROR), filename, GetLastError());
} else
return RC_FX;
} // endif Memory
CloseFileHandle(hFile); // Not used anymore
hFile = INVALID_HANDLE_VALUE; // For Fblock
/*********************************************************************/
/* Parse the json file and allocate its tree structure. */
/*********************************************************************/
g->Message[0] = 0;
jsp = Top = ParseJson(g, memory, len, Pretty);
CloseMemMap(memory, len);
Txfp->CloseTableFile(g, false);
Mode = mode; // Restore saved Mode
if (!jsp && g->Message[0])
return RC_FX;
@@ -1707,9 +1584,9 @@ int TDBJSON::MakeDocument(PGLOBAL g)
} // endif jsp
Done = true;
return RC_OK;
} // end of MakeDocument
#endif // 0
/***********************************************************************/
/* JSON Cardinality: returns table size in number of rows. */