From 671d9b6c611be423246b562878da2a5788cc327d Mon Sep 17 00:00:00 2001
From: Olivier Bertrand <bertrandop@gmail.com>
Date: Thu, 16 Jul 2020 16:30:54 +0200
Subject: [PATCH] - Fix MDEV-22571 and MDEV-22572. Allow multiple ZIP table  
 and enable using special column in them.   modified:  
 storage/connect/tabzip.cpp   modified:   storage/connect/tabzip.h

- Fix some compiler errors
  modified:   storage/connect/tabcmg.cpp
---
 storage/connect/connect.cc    |   1 -
 storage/connect/filamdbf.cpp  | 195 +++++++++++++++++------
 storage/connect/filamdbf.h    |   2 +-
 storage/connect/filamzip.cpp  | 282 ++++++++++++++++++++++++++++++++--
 storage/connect/filamzip.h    |  38 ++++-
 storage/connect/ha_connect.cc |  19 +--
 storage/connect/mongo.cpp     |   1 -
 storage/connect/mongo.h       |   1 -
 storage/connect/plgxml.cpp    |   2 +-
 storage/connect/tabcmg.cpp    |   2 +
 storage/connect/tabdos.cpp    |  36 +++--
 storage/connect/tabdos.h      |   1 +
 storage/connect/tabfix.h      |  18 ++-
 storage/connect/tabjson.cpp   |   1 +
 storage/connect/tabjson.h     |   1 -
 storage/connect/tabzip.cpp    |  13 +-
 storage/connect/tabzip.h      |   2 +
 17 files changed, 509 insertions(+), 106 deletions(-)

diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
index dfc619cf4af..2a0f2ed037f 100644
--- a/storage/connect/connect.cc
+++ b/storage/connect/connect.cc
@@ -355,7 +355,6 @@ bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
 		} // endif mode
 
 		rcop = false;
-
 	} catch (int n) {
 		if (trace(1))
 			htrc("Exception %d: %s\n", n, g->Message);
diff --git a/storage/connect/filamdbf.cpp b/storage/connect/filamdbf.cpp
index c8bab2b53a4..542159fd172 100644
--- a/storage/connect/filamdbf.cpp
+++ b/storage/connect/filamdbf.cpp
@@ -49,6 +49,7 @@
 #include "global.h"
 #include "plgdbsem.h"
 #include "filamdbf.h"
+#include "filamzip.h"
 #include "tabdos.h"
 #include "valblk.h"
 #define  NO_FUNC
@@ -139,7 +140,7 @@ static int dbfhead(PGLOBAL g, FILE *file, PCSZ fn, DBFHEADER *buf)
   if (fread(buf, HEADLEN, 1, file) != 1) {
     strcpy(g->Message, MSG(NO_READ_32));
     return RC_NF;
-    } // endif fread
+  } // endif fread
 
   // Check first byte to be sure of .dbf type
   if ((buf->Version & 0x03) != DBFTYPE) {
@@ -149,7 +150,7 @@ static int dbfhead(PGLOBAL g, FILE *file, PCSZ fn, DBFHEADER *buf)
     if ((buf->Version & 0x30) == 0x30) {
       strcpy(g->Message, MSG(FOXPRO_FILE));
       dbc = 264;             // FoxPro database container
-      } // endif Version
+    } // endif Version
 
   } else
     strcpy(g->Message, MSG(DBASE_FILE));
@@ -158,12 +159,12 @@ static int dbfhead(PGLOBAL g, FILE *file, PCSZ fn, DBFHEADER *buf)
   if (fseek(file, buf->Headlen() - dbc, SEEK_SET) != 0) {
     sprintf(g->Message, MSG(BAD_HEADER), fn);
     return RC_FX;
-    } // endif fseek
+  } // endif fseek
 
   if (fread(&endmark, 2, 1, file) != 1) {
     strcpy(g->Message, MSG(BAD_HEAD_END));
     return RC_FX;
-    } // endif fread
+  } // endif fread
 
   // Some files have just 1D others have 1D00 following fields
   if (endmark[0] != EOH && endmark[1] != EOH) {
@@ -172,7 +173,7 @@ static int dbfhead(PGLOBAL g, FILE *file, PCSZ fn, DBFHEADER *buf)
     if (rc == RC_OK)
       return RC_FX;
 
-    } // endif endmark
+  } // endif endmark
 
   // Calculate here the number of fields while we have the dbc info
   buf->SetFields((buf->Headlen() - dbc - 1) / 32);
@@ -180,13 +181,58 @@ static int dbfhead(PGLOBAL g, FILE *file, PCSZ fn, DBFHEADER *buf)
   return rc;
   } // end of dbfhead
 
+/****************************************************************************/
+/*  dbfields: Analyze a DBF header and set the table fields number.         */
+/*  Parameters:                                                             */
+/*      PGLOBAL g       -- pointer to the CONNECT Global structure          */
+/*      DBFHEADER *hdrp -- pointer to _dbfheader structure                  */
+/*  Returns:                                                                */
+/*      RC_OK, RC_INFO, or RC_FX if error.                                  */
+/****************************************************************************/
+static int dbfields(PGLOBAL g, DBFHEADER* hdrp)
+{
+	char* endmark;
+	int   dbc = 2, rc = RC_OK;
+
+	*g->Message = '\0';
+
+	// Check first byte to be sure of .dbf type
+	if ((hdrp->Version & 0x03) != DBFTYPE) {
+		strcpy(g->Message, MSG(NOT_A_DBF_FILE));
+		rc = RC_INFO;
+
+		if ((hdrp->Version & 0x30) == 0x30) {
+			strcpy(g->Message, MSG(FOXPRO_FILE));
+			dbc = 264;             // FoxPro database container
+		} // endif Version
+
+	} else
+		strcpy(g->Message, MSG(DBASE_FILE));
+
+	// Check last byte(s) of header
+	endmark = (char*)hdrp + hdrp->Headlen() - dbc;
+
+	// Some headers just have 1D others have 1D00 following fields
+	if (endmark[0] != EOH && endmark[1] != EOH) {
+		sprintf(g->Message, MSG(NO_0DH_HEAD), dbc);
+
+		if (rc == RC_OK)
+			return RC_FX;
+
+	} // endif endmark
+
+	// Calculate here the number of fields while we have the dbc info
+	hdrp->SetFields((hdrp->Headlen() - dbc - 1) / 32);
+	return rc;
+} // end of dbfields
+
 /* -------------------------- Function DBFColumns ------------------------- */
 
 /****************************************************************************/
 /*  DBFColumns: constructs the result blocks containing the description     */
 /*  of all the columns of a DBF file that will be retrieved by #GetData.    */
 /****************************************************************************/
-PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
+PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, PTOS topt, bool info)
   {
   int  buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING,
                    TYPE_INT,    TYPE_INT,   TYPE_SHORT};
@@ -196,10 +242,12 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
   char       buf[2], filename[_MAX_PATH];
   int        ncol = sizeof(buftyp) / sizeof(int);
   int        rc, type, len, field, fields;
-  bool       bad;
-  DBFHEADER  mainhead;
-  DESCRIPTOR thisfield;
-  FILE      *infile = NULL;
+  bool       bad, mul;
+	PCSZ       target, pwd;
+  DBFHEADER  mainhead, *hp;
+	DESCRIPTOR thisfield, *tfp;
+	FILE      *infile = NULL;
+	UNZIPUTL  *zutp = NULL;
   PQRYRES    qrp;
   PCOLRES    crp;
 
@@ -217,21 +265,55 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
     /************************************************************************/
     PlugSetPath(filename, fn, dp);
 
-    if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "rb")))
-      return NULL;
+		if (topt->zipped) {
+			target = GetStringTableOption(g, topt, "Entry", NULL);
+			mul = (target && *target) ? strchr(target, '*') || strchr(target, '?')
+				                        : false;
+			mul = GetBooleanTableOption(g, topt, "Mulentries", mul);
 
-    /************************************************************************/
-    /*  Get the first 32 bytes of the header.                               */
-    /************************************************************************/
-    if ((rc = dbfhead(g, infile, filename, &mainhead)) == RC_FX) {
-      fclose(infile);
-      return NULL;
-      } // endif dbfhead
+			if (mul) {
+				strcpy(g->Message, "Cannot find column definition for multiple entries");
+				return NULL;
+			} // endif Multiple
 
-    /************************************************************************/
-    /*  Allocate the structures used to refer to the result set.            */
-    /************************************************************************/
-    fields = mainhead.Fields();
+			pwd = GetStringTableOption(g, topt, "Password", NULL);
+			zutp = new(g) UNZIPUTL(target, pwd, mul);
+
+			if (!zutp->OpenTable(g, MODE_READ, filename))
+				hp = (DBFHEADER*)zutp->memory;
+			else
+				return NULL;
+
+			/**********************************************************************/
+			/*  Set the table fields number.                                      */
+			/**********************************************************************/
+			if ((rc = dbfields(g, hp)) == RC_FX) {
+				zutp->close();
+				return NULL;
+			} // endif dbfields
+
+			tfp = (DESCRIPTOR*)hp;
+		} else {
+			if (!(infile = global_fopen(g, MSGID_CANNOT_OPEN, filename, "rb")))
+				return NULL;
+		  else
+			  hp = &mainhead;
+
+			/**********************************************************************/
+			/*  Get the first 32 bytes of the header.                             */
+			/**********************************************************************/
+			if ((rc = dbfhead(g, infile, filename, hp)) == RC_FX) {
+				fclose(infile);
+				return NULL;
+			} // endif dbfhead
+
+			tfp = &thisfield;
+		} // endif zipped
+
+		/************************************************************************/
+		/*  Get the number of the table fields.                                 */
+		/************************************************************************/
+		fields = hp->Fields();
   } else
     fields = 0;
 
@@ -241,19 +323,21 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
   if (info || !qrp) {
   	if (infile)
       fclose(infile);
+		else if (zutp)
+			zutp->close();
       
     return qrp;
-    } // endif info
+  } // endif info
 
   if (trace(1)) {
     htrc("Structure of %s\n", filename);
     htrc("headlen=%hd reclen=%hd degree=%d\n",
-          mainhead.Headlen(), mainhead.Reclen(), fields);
-    htrc("flags(iem)=%d,%d,%d cp=%d\n", mainhead.Incompleteflag,
-          mainhead.Encryptflag, mainhead.Mdxflag, mainhead.Language);
+          hp->Headlen(), hp->Reclen(), fields);
+    htrc("flags(iem)=%d,%d,%d cp=%d\n", hp->Incompleteflag,
+          hp->Encryptflag, hp->Mdxflag, hp->Language);
     htrc("%hd records, last changed %02d/%02d/%d\n",
-          mainhead.Records(), mainhead.Filedate[1], mainhead.Filedate[2],
-          mainhead.Filedate[0] + (mainhead.Filedate[0] <= 30) ? 2000 : 1900);
+          hp->Records(), hp->Filedate[1], hp->Filedate[2],
+          hp->Filedate[0] + (hp->Filedate[0] <= 30) ? 2000 : 1900);
     htrc("Field    Type  Offset  Len  Dec  Set  Mdx\n");
     } // endif trace
 
@@ -265,21 +349,24 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
   for (field = 0; field < fields; field++) {
     bad = FALSE;
 
-    if (fread(&thisfield, HEADLEN, 1, infile) != 1) {
+		if (topt->zipped) {
+			tfp = (DESCRIPTOR*)((char*)tfp + HEADLEN);
+		} else if (fread(tfp, HEADLEN, 1, infile) != 1) {
       sprintf(g->Message, MSG(ERR_READING_REC), field+1, fn);
       goto err;
-    } else
-      len = thisfield.Length;
+    } // endif fread
+
+    len = tfp->Length;
 
     if (trace(1))
       htrc("%-11s %c  %6ld  %3d   %2d  %3d  %3d\n",
-           thisfield.Name, thisfield.Type, thisfield.Offset, len,
-           thisfield.Decimals, thisfield.Setfield, thisfield.Mdxfield);
+           tfp->Name, tfp->Type, tfp->Offset, len,
+           tfp->Decimals, tfp->Setfield, tfp->Mdxfield);
 
     /************************************************************************/
     /*  Now get the results into blocks.                                    */
     /************************************************************************/
-    switch (thisfield.Type) {
+    switch (tfp->Type) {
       case 'C':                      // Characters
       case 'L':                      // Logical 'T' or 'F' or space
 				type = TYPE_STRING;
@@ -294,7 +381,7 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
 			//	type = TYPE_INT;
 			//	break;
       case 'N':
-        type = (thisfield.Decimals) ? TYPE_DOUBLE
+        type = (tfp->Decimals) ? TYPE_DOUBLE
              : (len > 10) ? TYPE_BIGINT : TYPE_INT;
         break;
       case 'F':											 // Float
@@ -306,8 +393,8 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
         break;
       default:
         if (!info) {
-          sprintf(g->Message, MSG(BAD_DBF_TYPE), thisfield.Type
-                                               , thisfield.Name);
+          sprintf(g->Message, MSG(BAD_DBF_TYPE), tfp->Type
+                                               , tfp->Name);
           goto err;
           } // endif info
 
@@ -316,27 +403,31 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
       } // endswitch Type
 
     crp = qrp->Colresp;                    // Column Name
-    crp->Kdata->SetValue(thisfield.Name, field);
+    crp->Kdata->SetValue(tfp->Name, field);
     crp = crp->Next;                       // Data Type
     crp->Kdata->SetValue((int)type, field);
     crp = crp->Next;                       // Type Name
 
     if (bad) {
-      buf[0] = thisfield.Type;
+      buf[0] = tfp->Type;
       crp->Kdata->SetValue(buf, field);
     } else
       crp->Kdata->SetValue(GetTypeName(type), field);
 
     crp = crp->Next;                       // Precision
-    crp->Kdata->SetValue((int)thisfield.Length, field);
+    crp->Kdata->SetValue((int)tfp->Length, field);
     crp = crp->Next;                       // Length
-    crp->Kdata->SetValue((int)thisfield.Length, field);
+    crp->Kdata->SetValue((int)tfp->Length, field);
     crp = crp->Next;                       // Scale (precision)
-    crp->Kdata->SetValue((int)thisfield.Decimals, field);
+    crp->Kdata->SetValue((int)tfp->Decimals, field);
     } // endfor field
 
   qrp->Nblin = field;
-  fclose(infile);
+
+	if (infile)
+		fclose(infile);
+	else if (zutp)
+		zutp->close();
 
 #if 0
   if (info) {
@@ -347,9 +438,9 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
 
     sprintf(buf,
       "Ver=%02x ncol=%hu nlin=%u lrecl=%hu headlen=%hu date=%02d/%02d/%02d",
-      mainhead.Version, fields, mainhead.Records, mainhead.Reclen,
-      mainhead.Headlen, mainhead.Filedate[0], mainhead.Filedate[1],
-      mainhead.Filedate[2]);
+      hp->Version, fields, hp->Records, hp->Reclen,
+      hp->Headlen, hp->Filedate[0], hp->Filedate[1],
+      hp->Filedate[2]);
 
     strcat(g->Message, buf);
     } // endif info
@@ -360,9 +451,13 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info)
   /**************************************************************************/
   return qrp;
 
- err:
-  fclose(infile);
-  return NULL;
+err:
+	if (infile)
+		fclose(infile);
+	else if (zutp)
+		zutp->close();
+
+	return NULL;
   } // end of DBFColumns
 
 /* ---------------------------- Class DBFBASE ----------------------------- */
diff --git a/storage/connect/filamdbf.h b/storage/connect/filamdbf.h
index 640fc349b4c..dfe5cb5cfc4 100644
--- a/storage/connect/filamdbf.h
+++ b/storage/connect/filamdbf.h
@@ -19,7 +19,7 @@ typedef class DBMFAM  *PDBMFAM;
 /****************************************************************************/
 /*  Functions used externally.                                              */
 /****************************************************************************/
-PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, bool info);
+PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, PTOS tiop, bool info);
 
 /****************************************************************************/
 /*  This is the base class for dBASE file access methods.                   */
diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp
index e76dc496246..eeb23e1f053 100644
--- a/storage/connect/filamzip.cpp
+++ b/storage/connect/filamzip.cpp
@@ -1,11 +1,11 @@
 /*********** File AM Zip C++ Program Source Code File (.CPP) ***********/
 /* PROGRAM NAME: FILAMZIP                                              */
 /* -------------                                                       */
-/*  Version 1.3                                                        */
+/*  Version 1.4                                                        */
 /*                                                                     */
 /* COPYRIGHT:                                                          */
 /* ----------                                                          */
-/*  (C) Copyright to the author Olivier BERTRAND          2016-2017    */
+/*  (C) Copyright to the author Olivier BERTRAND          2016-2020    */
 /*                                                                     */
 /* WHAT THIS PROGRAM DOES:                                             */
 /* -----------------------                                             */
@@ -45,6 +45,62 @@
 
 #define WRITEBUFFERSIZE (16384)
 
+/****************************************************************************/
+/*  Definitions used for DBF tables.                                        */
+/****************************************************************************/
+#define HEADLEN       32            /* sizeof ( mainhead or thisfield )     */
+//efine MEMOLEN       10            /* length of memo field in .dbf         */
+#define DBFTYPE        3            /* value of bits 0 and 1 if .dbf        */
+#define EOH         0x0D            /* end-of-header marker in .dbf file    */
+
+/****************************************************************************/
+/*  First 32 bytes of a DBF table.                                          */
+/*  Note: some reserved fields are used here to store info (Fields)         */
+/****************************************************************************/
+typedef struct _dbfheader {
+	uchar  Version;                   /*  Version information flags           */
+	char   Filedate[3];               /*  date, YYMMDD, binary. YY=year-1900  */
+private:
+	/* The following four members are stored in little-endian format on disk  */
+	char   m_RecordsBuf[4];           /*  records in the file                 */
+	char   m_HeadlenBuf[2];           /*  bytes in the header                 */
+	char   m_ReclenBuf[2];            /*  bytes in a record                   */
+	char   m_FieldsBuf[2];            /*  Reserved but used to store fields   */
+public:
+	char   Incompleteflag;            /*  01 if incomplete, else 00           */
+	char   Encryptflag;               /*  01 if encrypted, else 00            */
+	char   Reserved2[12];             /*  for LAN use                         */
+	char   Mdxflag;                   /*  01 if production .mdx, else 00      */
+	char   Language;                  /*  Codepage                            */
+	char   Reserved3[2];
+
+	uint   Records(void) const { return uint4korr(m_RecordsBuf); }
+	ushort Headlen(void) const { return uint2korr(m_HeadlenBuf); }
+	ushort Reclen(void)  const { return uint2korr(m_ReclenBuf); }
+	ushort Fields(void)  const { return uint2korr(m_FieldsBuf); }
+
+	void   SetHeadlen(ushort num) { int2store(m_HeadlenBuf, num); }
+	void   SetReclen(ushort num) { int2store(m_ReclenBuf, num); }
+	void   SetFields(ushort num) { int2store(m_FieldsBuf, num); }
+} DBFHEADER;
+
+/****************************************************************************/
+/*  Column field descriptor of a .dbf file.                                 */
+/****************************************************************************/
+typedef struct _descriptor {
+	char  Name[11];                   /*  field name, in capitals, null filled*/
+	char  Type;                       /*  field type, C, D, F, L, M or N      */
+	uint  Offset;                     /*  used in memvars, not in files.      */
+	uchar Length;                     /*  field length                        */
+	uchar Decimals;                   /*  number of decimal places            */
+	short Reserved4;
+	char  Workarea;                   /*  ???                                 */
+	char  Reserved5[2];
+	char  Setfield;                   /*  ???                                 */
+	char  Reserved6[7];
+	char  Mdxfield;                   /* 01 if tag field in production .mdx   */
+} DESCRIPTOR;
+
 bool ZipLoadFile(PGLOBAL g, PCSZ zfn, PCSZ fn, PCSZ entry, bool append, bool mul);
 
 /***********************************************************************/
@@ -214,10 +270,21 @@ bool ZipLoadFile(PGLOBAL g, PCSZ zfn, PCSZ fn, PCSZ entry, bool append, bool mul
 
 	buf = (char*)PlugSubAlloc(g, NULL, WRITEBUFFERSIZE);
 
-	if (mul)
-		err = ZipFiles(g, zutp, fn, buf);
-	else
-	  err = ZipFile(g, zutp, fn, entry, buf);
+	if (!mul) {
+		PCSZ entp;
+
+		if (!entry) {    // entry defaults to the file name
+			char* p = strrchr((char*)fn, '/');
+#if defined(__WIN__)
+			if (!p) p = strrchr((char*)fn, '\\');
+#endif  //  __WIN__
+			entp = (p) ? p + 1 : entry;
+		} else
+			entp = entry;
+
+		err = ZipFile(g, zutp, fn, entp, buf);
+	} else
+	  err = ZipFiles(g, zutp, fn, buf);
 
 	zutp->close();
 	return err;
@@ -232,6 +299,7 @@ ZIPUTIL::ZIPUTIL(PCSZ tgt)
 {
 	zipfile = NULL;
 	target = tgt;
+	pwd = NULL;
 	fp = NULL;
 	entryopen = false;
 } // end of ZIPUTIL standard constructor
@@ -241,6 +309,7 @@ ZIPUTIL::ZIPUTIL(ZIPUTIL *zutp)
 {
 	zipfile = zutp->zipfile;
 	target = zutp->target;
+	pwd = zutp->pwd;
 	fp = zutp->fp;
 	entryopen = zutp->entryopen;
 } // end of UNZIPUTL copy constructor
@@ -385,11 +454,11 @@ void ZIPUTIL::closeEntry()
 /***********************************************************************/
 /*  Constructors.                                                      */
 /***********************************************************************/
-UNZIPUTL::UNZIPUTL(PCSZ tgt, bool mul)
+UNZIPUTL::UNZIPUTL(PCSZ tgt, PCSZ pw, bool mul)
 {
 	zipfile = NULL;
 	target = tgt;
-	pwd = NULL;
+	pwd = pw;
 	fp = NULL;
 	memory = NULL;
 	size = 0;
@@ -959,7 +1028,7 @@ int UZXFAM::Cardinality(PGLOBAL g)
 } // end of Cardinality
 
 /***********************************************************************/
-/*  OpenTableFile: Open a DOS/UNIX table file from a ZIP file.         */
+/*  OpenTableFile: Open a FIX/UNIX table file from a ZIP file.         */
 /***********************************************************************/
 bool UZXFAM::OpenTableFile(PGLOBAL g)
 {
@@ -1015,6 +1084,197 @@ int UZXFAM::GetNext(PGLOBAL g)
 	return RC_OK;
 } // end of GetNext
 
+/* -------------------------- class UZDFAM --------------------------- */
+
+/***********************************************************************/
+/*  Constructors.                                                      */
+/***********************************************************************/
+UZDFAM::UZDFAM(PDOSDEF tdp) : DBMFAM(tdp)
+{
+	zutp = NULL;
+	tdfp = tdp;
+	//target = tdp->GetEntry();
+	//mul = tdp->GetMul();
+	//Lrecl = tdp->GetLrecl();
+} // end of UZXFAM standard constructor
+
+UZDFAM::UZDFAM(PUZDFAM txfp) : DBMFAM(txfp)
+{
+	zutp = txfp->zutp;
+	tdfp = txfp->tdfp;
+	//target = txfp->target;
+	//mul = txfp->mul;
+	//Lrecl = txfp->Lrecl;
+} // end of UZXFAM copy constructor
+
+#if 0
+/****************************************************************************/
+/*  dbfhead: Routine to analyze a DBF header.                               */
+/*  Parameters:                                                             */
+/*      PGLOBAL g       -- pointer to the CONNECT Global structure          */
+/*      DBFHEADER *hdrp -- pointer to _dbfheader structure                  */
+/*  Returns:                                                                */
+/*      RC_OK, RC_NF, RC_INFO, or RC_FX if error.                           */
+/*  Side effects:                                                           */
+/*      Set the fields number in the header.                                */
+/****************************************************************************/
+int UZDFAM::dbfhead(PGLOBAL g, void* buf)
+{
+	char *endmark;
+	int   dbc = 2, rc = RC_OK;
+	DBFHEADER* hdrp = (DBFHEADER*)buf;
+
+	*g->Message = '\0';
+
+	// Check first byte to be sure of .dbf type
+	if ((hdrp->Version & 0x03) != DBFTYPE) {
+		strcpy(g->Message, MSG(NOT_A_DBF_FILE));
+		rc = RC_INFO;
+
+		if ((hdrp->Version & 0x30) == 0x30) {
+			strcpy(g->Message, MSG(FOXPRO_FILE));
+			dbc = 264;             // FoxPro database container
+		} // endif Version
+
+	} else
+		strcpy(g->Message, MSG(DBASE_FILE));
+
+	// Check last byte(s) of header
+	endmark = (char*)hdrp + hdrp->Headlen() - dbc;
+
+	// Some headers just have 1D others have 1D00 following fields
+	if (endmark[0] != EOH && endmark[1] != EOH) {
+		sprintf(g->Message, MSG(NO_0DH_HEAD), dbc);
+
+		if (rc == RC_OK)
+			return RC_FX;
+
+	} // endif endmark
+
+	// Calculate here the number of fields while we have the dbc info
+	hdrp->SetFields((hdrp->Headlen() - dbc - 1) / 32);
+	return rc;
+} // end of dbfhead
+
+/****************************************************************************/
+/*  ScanHeader: scan the DBF file header for number of records, record size,*/
+/*  and header length. Set Records, check that Reclen is equal to lrecl and */
+/*  return the header length or 0 in case of error.                         */
+/****************************************************************************/
+int UZDFAM::ScanHeader(PGLOBAL g, int* rln)
+{
+	int       rc;
+	DBFHEADER header;
+
+	/************************************************************************/
+	/*  Get the first 32 bytes of the header.                               */
+	/************************************************************************/
+	rc = dbfhead(g, &header);
+
+	if (rc == RC_FX)
+		return -1;
+
+	*rln = (int)header.Reclen();
+	Records = (int)header.Records();
+	return (int)header.Headlen();
+} // end of ScanHeader
+#endif // 0
+
+/***********************************************************************/
+/*  ZIP GetFileLength: returns file size in number of bytes.           */
+/***********************************************************************/
+int UZDFAM::GetFileLength(PGLOBAL g)
+{
+	int len;
+
+	if (!zutp && OpenTableFile(g))
+		return 0;
+
+	if (zutp->entryopen)
+		len = zutp->size;
+	else
+		len = 0;
+
+	return len;
+} // end of GetFileLength
+
+/***********************************************************************/
+/*  ZIP Cardinality: return the number of rows if possible.            */
+/***********************************************************************/
+int UZDFAM::Cardinality(PGLOBAL g)
+{
+	if (!g)
+		return 1;
+
+	int card = -1;
+	int len = GetFileLength(g);
+
+	card = Records;
+
+	// Set number of blocks for later use
+	Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
+	return card;
+} // end of Cardinality
+
+/***********************************************************************/
+/*  OpenTableFile: Open a DBF table file from a ZIP file.              */
+/***********************************************************************/
+bool UZDFAM::OpenTableFile(PGLOBAL g)
+{
+	// May have been already opened in GetFileLength
+	if (!zutp || !zutp->zipfile) {
+		char    filename[_MAX_PATH];
+		MODE    mode = Tdbp->GetMode();
+
+		/*********************************************************************/
+		/*  Allocate the ZIP utility class.                                  */
+		/*********************************************************************/
+		if (!zutp)
+			zutp = new(g)UNZIPUTL(tdfp);
+
+		//  We used the file name relative to recorded datapath
+		PlugSetPath(filename, To_File, Tdbp->GetPath());
+
+		if (!zutp->OpenTable(g, mode, filename)) {
+			// The pseudo "buffer" is here the entire real buffer
+			Memory = zutp->memory;
+			Top = Memory + zutp->size;
+			To_Fb = zutp->fp;                           // Useful when closing
+			return AllocateBuffer(g);
+		} else
+			return true;
+
+	} else
+		Reset();
+
+	return false;
+} // end of OpenTableFile
+
+/***********************************************************************/
+/*  GetNext: go to next entry.                                         */
+/***********************************************************************/
+int UZDFAM::GetNext(PGLOBAL g)
+{
+	int rc = zutp->nextEntry(g);
+
+	if (rc != RC_OK)
+		return rc;
+
+	int len = zutp->size;
+
+#if 0
+	if (len % Lrecl) {
+		sprintf(g->Message, MSG(NOT_FIXED_LEN), zutp->fn, len, Lrecl);
+		return RC_FX;
+	}	// endif size
+#endif // 0
+
+	Memory = zutp->memory;
+	Top = Memory + len;
+	Rewind();
+	return RC_OK;
+} // end of GetNext
+
 /* -------------------------- class ZIPFAM --------------------------- */
 
 /***********************************************************************/
@@ -1045,7 +1305,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g)
 		strcpy(g->Message, "No insert into existing zip file");
 		return true;
 	} else if (append && len > 0) {
-		UNZIPUTL *zutp = new(g) UNZIPUTL(target, false);
+		UNZIPUTL *zutp = new(g) UNZIPUTL(target, NULL, false);
 
 		if (!zutp->IsInsertOk(g, filename)) {
 			strcpy(g->Message, "No insert into existing entry");
@@ -1129,7 +1389,7 @@ bool ZPXFAM::OpenTableFile(PGLOBAL g)
 		strcpy(g->Message, "No insert into existing zip file");
 		return true;
 	} else if (append && len > 0) {
-		UNZIPUTL *zutp = new(g) UNZIPUTL(target, false);
+		UNZIPUTL *zutp = new(g) UNZIPUTL(target, NULL, false);
 
 		if (!zutp->IsInsertOk(g, filename)) {
 			strcpy(g->Message, "No insert into existing entry");
diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h
index be17d954728..7ff1fb0a543 100644
--- a/storage/connect/filamzip.h
+++ b/storage/connect/filamzip.h
@@ -1,7 +1,7 @@
 /************** filamzip H Declares Source Code File (.H) **************/
-/*  Name: filamzip.h   Version 1.2                                     */
+/*  Name: filamzip.h   Version 1.3                                     */
 /*                                                                     */
-/*  (C) Copyright to the author Olivier BERTRAND          2016-2017    */
+/*  (C) Copyright to the author Olivier BERTRAND          2016-2020    */
 /*                                                                     */
 /*  This file contains the ZIP file access method classes declares.    */
 /***********************************************************************/
@@ -11,6 +11,7 @@
 #include "block.h"
 #include "filamap.h"
 #include "filamfix.h"
+#include "filamdbf.h"
 #include "zip.h"
 #include "unzip.h"
 
@@ -18,6 +19,7 @@
 
 typedef class UNZFAM *PUNZFAM;
 typedef class UZXFAM *PUZXFAM;
+typedef class UZDFAM* PUZDFAM;
 typedef class ZIPFAM *PZIPFAM;
 typedef class ZPXFAM *PZPXFAM;
 
@@ -53,7 +55,7 @@ class DllExport ZIPUTIL : public BLOCK {
 class DllExport UNZIPUTL : public BLOCK {
  public:
 	// Constructor
-  UNZIPUTL(PCSZ tgt, bool mul);
+  UNZIPUTL(PCSZ tgt, PCSZ pw, bool mul);
   UNZIPUTL(PDOSDEF tdp);
 
 	// Implementation
@@ -143,6 +145,36 @@ class DllExport UZXFAM : public MPXFAM {
 	PDOSDEF   tdfp;
 }; // end of UZXFAM
 
+/***********************************************************************/
+/*  This is the fixed unzip file access method.                        */
+/***********************************************************************/
+class DllExport UZDFAM : public DBMFAM {
+	//friend class UNZFAM;
+public:
+	// Constructors
+	UZDFAM(PDOSDEF tdp);
+	UZDFAM(PUZDFAM txfp);
+
+	// Implementation
+	virtual AMT  GetAmType(void) { return TYPE_AM_ZIP; }
+	virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)UZDFAM(this); }
+
+	// Methods
+	virtual int  GetFileLength(PGLOBAL g);
+	virtual int  Cardinality(PGLOBAL g);
+	virtual bool OpenTableFile(PGLOBAL g);
+	virtual int  GetNext(PGLOBAL g);
+	//virtual int  ReadBuffer(PGLOBAL g);
+
+protected:
+	int dbfhead(PGLOBAL g, void* buf);
+	int ScanHeader(PGLOBAL g, int* rln);
+
+	// Members
+	UNZIPUTL* zutp;
+	PDOSDEF   tdfp;
+}; // end of UZDFAM
+
 /***********************************************************************/
 /*  This is the zip file access method.                                */
 /***********************************************************************/
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index a111082e786..bb56d6cd655 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -4507,12 +4507,12 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn, bool quick)
 		case TAB_DIR:
 		case TAB_ZIP:
 		case TAB_OEM:
-      if (table && table->pos_in_table_list) // if SELECT
-      {
-        //Switch_to_definer_security_ctx backup_ctx(thd, table->pos_in_table_list);
+      if (table && table->pos_in_table_list) {		// if SELECT
+#if MYSQL_VERSION_ID > 100200
+				Switch_to_definer_security_ctx backup_ctx(thd, table->pos_in_table_list);
+#endif // VERSION_ID > 100200
         return check_global_access(thd, FILE_ACL);
-      }
-      else
+      }	else
         return check_global_access(thd, FILE_ACL);
     case TAB_ODBC:
 		case TAB_JDBC:
@@ -4528,7 +4528,7 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn, bool quick)
     case TAB_VIR:
 			// This is temporary until a solution is found
 			return false;
-    } // endswitch type
+  } // endswitch type
 
   my_printf_error(ER_UNKNOWN_ERROR, "check_privileges failed", MYF(0));
   return true;
@@ -5882,7 +5882,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
 
 			} else switch (ttp) {
 				case TAB_DBF:
-					qrp= DBFColumns(g, dpath, fn, fnc == FNC_COL);
+					qrp= DBFColumns(g, dpath, fn, topt, fnc == FNC_COL);
 					break;
 #if defined(ODBC_SUPPORT)
 				case TAB_ODBC:
@@ -6733,11 +6733,6 @@ int ha_connect::create(const char *name, TABLE *table_arg,
 			PCSZ m= GetListOption(g, "Mulentries", options->oplist, "NO");
 			bool mul= *m == '1' || *m == 'Y' || *m == 'y' || !stricmp(m, "ON");
 
-			if (!entry && !mul) {
-				my_message(ER_UNKNOWN_ERROR, "Missing entry name", MYF(0));
-				DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
-			}	// endif entry
-
 			strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
 			PlugSetPath(zbuf, options->filename, dbpath);
 			PlugSetPath(buf, fn, dbpath);
diff --git a/storage/connect/mongo.cpp b/storage/connect/mongo.cpp
index 53e2bf377c4..bd3d3b893c1 100644
--- a/storage/connect/mongo.cpp
+++ b/storage/connect/mongo.cpp
@@ -380,7 +380,6 @@ MGODEF::MGODEF(void)
 	Uri = NULL;
 	Colist = NULL;
 	Filter = NULL;
-	Level = 0;
 	Base = 0;
 	Version = 0;
 	Pipe = false;
diff --git a/storage/connect/mongo.h b/storage/connect/mongo.h
index 97c391a217f..dcefac372c0 100644
--- a/storage/connect/mongo.h
+++ b/storage/connect/mongo.h
@@ -82,7 +82,6 @@ protected:
 	PSZ   Wrapname;               /* Java wrapper name                   */
 	PCSZ  Colist;                 /* Options list                        */
 	PCSZ  Filter;									/* Filtering query                     */
-	int   Level;                  /* Used for catalog table              */
 	int   Base;                   /* The array index base                */
 	int   Version;                /* The Java driver version             */
 	bool  Pipe;                   /* True is Colist is a pipeline        */
diff --git a/storage/connect/plgxml.cpp b/storage/connect/plgxml.cpp
index f3d3a010266..8c5cc261899 100644
--- a/storage/connect/plgxml.cpp
+++ b/storage/connect/plgxml.cpp
@@ -49,7 +49,7 @@ bool XMLDOCUMENT::InitZip(PGLOBAL g, PCSZ entry)
 {
 #if defined(ZIP_SUPPORT)
 	bool mul = (entry) ? strchr(entry, '*') || strchr(entry, '?') : false;
-	zip = new(g) UNZIPUTL(entry, mul);
+	zip = new(g) UNZIPUTL(entry, NULL, mul);
 	return zip == NULL;
 #else   // !ZIP_SUPPORT
 	sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
diff --git a/storage/connect/tabcmg.cpp b/storage/connect/tabcmg.cpp
index b9b7f6e4b60..f2ff721627c 100644
--- a/storage/connect/tabcmg.cpp
+++ b/storage/connect/tabcmg.cpp
@@ -26,6 +26,8 @@
 #include "tabmul.h"
 #include "filter.h"
 
+PQRYRES MGOColumns(PGLOBAL g, PCSZ db, PCSZ uri, PTOS topt, bool info);
+
 /* -------------------------- Class CMGDISC -------------------------- */
 
 /***********************************************************************/
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index 8efe2aad702..b3147bb7357 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -1,11 +1,11 @@
 /************* TabDos C++ Program Source Code File (.CPP) **************/
 /* PROGRAM NAME: TABDOS                                                */
 /* -------------                                                       */
-/*  Version 4.9.4                                                      */
+/*  Version 4.9.5                                                      */
 /*                                                                     */
 /* COPYRIGHT:                                                          */
 /* ----------                                                          */
-/*  (C) Copyright to the author Olivier BERTRAND          1998-2019    */
+/*  (C) Copyright to the author Olivier BERTRAND          1998-2020    */
 /*                                                                     */
 /* WHAT THIS PROGRAM DOES:                                             */
 /* -----------------------                                             */
@@ -359,7 +359,26 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
   /*  Allocate table and file processing class of the proper type.     */
   /*  Column blocks will be allocated only when needed.                */
   /*********************************************************************/
-	if (Zipped) {
+	if (Recfm == RECFM_DBF) {
+		if (Catfunc == FNC_NO) {
+			if (Zipped) {
+				if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
+					txfp = new(g) UZDFAM(this);
+				}	else {
+					strcpy(g->Message, "Zipped DBF tables are read only");
+					return NULL;
+				}	// endif's mode
+
+			} else if (map)
+				txfp = new(g) DBMFAM(this);
+			else
+				txfp = new(g) DBFFAM(this);
+
+			tdbp = new(g) TDBFIX(this, txfp);
+		} else
+			tdbp = new(g) TDBDCL(this);    // Catfunc should be 'C'
+
+	} else if (Zipped) {
 #if defined(ZIP_SUPPORT)
 		if (Recfm == RECFM_VAR) {
 			if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
@@ -389,17 +408,6 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
 		sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
 		return NULL;
 #endif  // !ZIP_SUPPORT
-	} else if (Recfm == RECFM_DBF) {
-    if (Catfunc == FNC_NO) {
-      if (map)
-        txfp = new(g) DBMFAM(this);
-      else
-        txfp = new(g) DBFFAM(this);
-
-      tdbp = new(g) TDBFIX(this, txfp);
-    } else                   // Catfunc should be 'C'
-      tdbp = new(g) TDBDCL(this);
-
   } else if (Recfm != RECFM_VAR && Compressed < 2) {
     if (Huge)
       txfp = new(g) BGXFAM(this);
diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h
index 207a1277fce..80dfe63845d 100644
--- a/storage/connect/tabdos.h
+++ b/storage/connect/tabdos.h
@@ -30,6 +30,7 @@ class DllExport DOSDEF : public TABDEF {  /* Logical table description */
   friend class DBFBASE;
 	friend class UNZIPUTL;
 	friend class JSONCOL;
+	friend class TDBDCL;
  public:
   // Constructor
   DOSDEF(void);
diff --git a/storage/connect/tabfix.h b/storage/connect/tabfix.h
index 53c0af1c422..5f859a2bffe 100644
--- a/storage/connect/tabfix.h
+++ b/storage/connect/tabfix.h
@@ -98,18 +98,20 @@ class DllExport BINCOL : public DOSCOL {
 /*  This is the class declaration for the DBF columns catalog table.   */
 /***********************************************************************/
 class TDBDCL : public TDBCAT {
- public:
-  // Constructor
-   TDBDCL(PDOSDEF tdp) : TDBCAT(tdp) {Fn = tdp->GetFn();}
+public:
+	// Constructor
+	TDBDCL(PDOSDEF tdp) : TDBCAT(tdp)
+	  {Fn = tdp->GetFn(); Topt = tdp->GetTopt();}
 
- protected:
+protected:
 	// Specific routines
-  virtual PQRYRES GetResult(PGLOBAL g) 
-      {return DBFColumns(g, ((PTABDEF)To_Def)->GetPath(), Fn, false);}
+	virtual PQRYRES GetResult(PGLOBAL g)
+	  {return DBFColumns(g, ((PTABDEF)To_Def)->GetPath(), Fn, Topt, false);}
 
-  // Members
+	// Members
 	PCSZ Fn;                    // The DBF file (path) name
-  }; // end of class TDBOCL
+	PTOS Topt;
+}; // end of class TDBOCL
 
 
 #endif // __TABFIX__
diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp
index 7e8d6c8d9f0..3b0d458a7a6 100644
--- a/storage/connect/tabjson.cpp
+++ b/storage/connect/tabjson.cpp
@@ -739,6 +739,7 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
 /***********************************************************************/
 TDBJSN::TDBJSN(PJDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
   {
+	G = NULL;
   Top = NULL;
   Row = NULL;
   Val = NULL;
diff --git a/storage/connect/tabjson.h b/storage/connect/tabjson.h
index 8721a2a5ab7..8c3f1013919 100644
--- a/storage/connect/tabjson.h
+++ b/storage/connect/tabjson.h
@@ -104,7 +104,6 @@ public:
 	PCSZ  Xcol;                   /* Name of expandable column           */
   int   Limit;                  /* Limit of multiple values            */
   int   Pretty;                 /* Depends on file structure           */
-  int   Level;                  /* Used for catalog table              */
   int   Base;                   /* The array index base                */
   bool  Strict;                 /* Strict syntax checking              */
 	char  Sep;                    /* The Jpath separator                 */
diff --git a/storage/connect/tabzip.cpp b/storage/connect/tabzip.cpp
index c026744dba8..d9c13e2a58a 100644
--- a/storage/connect/tabzip.cpp
+++ b/storage/connect/tabzip.cpp
@@ -23,6 +23,7 @@
 #include "filamzip.h"
 #include "resource.h"                        // for IDS_COLUMNS
 #include "tabdos.h"
+#include "tabmul.h"
 #include "tabzip.h"
 
 /* -------------------------- Class ZIPDEF --------------------------- */
@@ -41,7 +42,14 @@ bool ZIPDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
 /***********************************************************************/
 PTDB ZIPDEF::GetTable(PGLOBAL g, MODE m)
 {
-	return new(g) TDBZIP(this);
+	PTDB tdbp = NULL;
+
+	tdbp = new(g) TDBZIP(this);
+
+	if (Multiple)
+		tdbp = new(g) TDBMUL(tdbp);
+
+	return tdbp;
 } // end of GetTable
 
 /* ------------------------------------------------------------------- */
@@ -108,7 +116,7 @@ int TDBZIP::Cardinality(PGLOBAL g)
 
 			Cardinal = (err == UNZ_OK) ? (int)ginfo.number_entry : 0;
 		} else
-			Cardinal = 0;
+			Cardinal = 10;    // Dummy for multiple tables
 
 	} // endif Cardinal
 
@@ -187,6 +195,7 @@ int TDBZIP::DeleteDB(PGLOBAL g, int irc)
 void TDBZIP::CloseDB(PGLOBAL g)
 {
 	close();
+	nexterr = UNZ_OK;               // For multiple tables
 	Use = USE_READY;                // Just to be clean
 } // end of CloseDB
 
diff --git a/storage/connect/tabzip.h b/storage/connect/tabzip.h
index 32b15281f81..d36e4dc01d0 100644
--- a/storage/connect/tabzip.h
+++ b/storage/connect/tabzip.h
@@ -48,6 +48,8 @@ public:
 
 	// Implementation
 	virtual AMT  GetAmType(void) {return TYPE_AM_ZIP;}
+	virtual PCSZ GetFile(PGLOBAL) {return zfn;}
+	virtual void SetFile(PGLOBAL, PCSZ fn) {zfn = fn;}
 
 	// Methods
 	virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);