Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
06af0367
Commit
06af0367
authored
Oct 24, 2020
by
Oleksandr Byelkin
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'connect/10.1' into 10.1
parents
94b49357
671d9b6c
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
511 additions
and
108 deletions
+511
-108
storage/connect/connect.cc
storage/connect/connect.cc
+0
-1
storage/connect/filamdbf.cpp
storage/connect/filamdbf.cpp
+147
-52
storage/connect/filamdbf.h
storage/connect/filamdbf.h
+1
-1
storage/connect/filamzip.cpp
storage/connect/filamzip.cpp
+271
-11
storage/connect/filamzip.h
storage/connect/filamzip.h
+35
-3
storage/connect/ha_connect.cc
storage/connect/ha_connect.cc
+7
-12
storage/connect/mongo.cpp
storage/connect/mongo.cpp
+0
-1
storage/connect/mongo.h
storage/connect/mongo.h
+0
-1
storage/connect/plgxml.cpp
storage/connect/plgxml.cpp
+1
-1
storage/connect/tabcmg.cpp
storage/connect/tabcmg.cpp
+2
-0
storage/connect/tabdos.cpp
storage/connect/tabdos.cpp
+22
-14
storage/connect/tabdos.h
storage/connect/tabdos.h
+1
-0
storage/connect/tabfix.h
storage/connect/tabfix.h
+10
-8
storage/connect/tabjson.cpp
storage/connect/tabjson.cpp
+1
-0
storage/connect/tabjson.h
storage/connect/tabjson.h
+0
-1
storage/connect/tabzip.cpp
storage/connect/tabzip.cpp
+11
-2
storage/connect/tabzip.h
storage/connect/tabzip.h
+2
-0
No files found.
storage/connect/connect.cc
View file @
06af0367
...
...
@@ -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
);
...
...
storage/connect/filamdbf.cpp
View file @
06af0367
...
...
@@ -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
;
/************************************************************************/
/* Get the first 32 bytes of the header. */
/************************************************************************/
if
((
rc
=
dbfhead
(
g
,
infile
,
filename
,
&
mainhead
))
==
RC_FX
)
{
fclose
(
infile
);
return
NULL
;
}
// endif dbfhead
/************************************************************************/
/* Allocate the structures used to refer to the result set. */
/************************************************************************/
fields
=
mainhead
.
Fields
();
if
(
topt
->
zipped
)
{
target
=
GetStringTableOption
(
g
,
topt
,
"Entry"
,
NULL
);
mul
=
(
target
&&
*
target
)
?
strchr
(
target
,
'*'
)
||
strchr
(
target
,
'?'
)
:
false
;
mul
=
GetBooleanTableOption
(
g
,
topt
,
"Mulentries"
,
mul
);
if
(
mul
)
{
strcpy
(
g
->
Message
,
"Cannot find column definition for multiple entries"
);
return
NULL
;
}
// endif Multiple
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
"
,
t
hisfield
.
Name
,
thisfield
.
Type
,
thisfield
.
Offset
,
len
,
t
hisfield
.
Decimals
,
thisfield
.
Setfield
,
thisfield
.
Mdxfield
);
t
fp
->
Name
,
tfp
->
Type
,
tfp
->
Offset
,
len
,
t
fp
->
Decimals
,
tfp
->
Setfield
,
tfp
->
Mdxfield
);
/************************************************************************/
/* Now get the results into blocks. */
/************************************************************************/
switch
(
t
hisfield
.
Type
)
{
switch
(
t
fp
->
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
=
(
t
hisfield
.
Decimals
)
?
TYPE_DOUBLE
type
=
(
t
fp
->
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
),
t
hisfield
.
Type
,
t
hisfield
.
Name
);
sprintf
(
g
->
Message
,
MSG
(
BAD_DBF_TYPE
),
t
fp
->
Type
,
t
fp
->
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
(
t
hisfield
.
Name
,
field
);
crp
->
Kdata
->
SetValue
(
t
fp
->
Name
,
field
);
crp
=
crp
->
Next
;
// Data Type
crp
->
Kdata
->
SetValue
((
int
)
type
,
field
);
crp
=
crp
->
Next
;
// Type Name
if
(
bad
)
{
buf
[
0
]
=
t
hisfield
.
Type
;
buf
[
0
]
=
t
fp
->
Type
;
crp
->
Kdata
->
SetValue
(
buf
,
field
);
}
else
crp
->
Kdata
->
SetValue
(
GetTypeName
(
type
),
field
);
crp
=
crp
->
Next
;
// Precision
crp
->
Kdata
->
SetValue
((
int
)
t
hisfield
.
Length
,
field
);
crp
->
Kdata
->
SetValue
((
int
)
t
fp
->
Length
,
field
);
crp
=
crp
->
Next
;
// Length
crp
->
Kdata
->
SetValue
((
int
)
t
hisfield
.
Length
,
field
);
crp
->
Kdata
->
SetValue
((
int
)
t
fp
->
Length
,
field
);
crp
=
crp
->
Next
;
// Scale (precision)
crp
->
Kdata
->
SetValue
((
int
)
t
hisfield
.
Decimals
,
field
);
crp
->
Kdata
->
SetValue
((
int
)
t
fp
->
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 ----------------------------- */
...
...
storage/connect/filamdbf.h
View file @
06af0367
...
...
@@ -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. */
...
...
storage/connect/filamzip.cpp
View file @
06af0367
/*********** 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-20
17
*/
/* (C) Copyright to the author Olivier BERTRAND 2016-20
20
*/
/* */
/* 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"
);
...
...
storage/connect/filamzip.h
View file @
06af0367
/************** 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-20
17
*/
/* (C) Copyright to the author Olivier BERTRAND 2016-20
20
*/
/* */
/* 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. */
/***********************************************************************/
...
...
storage/connect/ha_connect.cc
View file @
06af0367
...
...
@@ -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
);
...
...
storage/connect/mongo.cpp
View file @
06af0367
...
...
@@ -380,7 +380,6 @@ MGODEF::MGODEF(void)
Uri
=
NULL
;
Colist
=
NULL
;
Filter
=
NULL
;
Level
=
0
;
Base
=
0
;
Version
=
0
;
Pipe
=
false
;
...
...
storage/connect/mongo.h
View file @
06af0367
...
...
@@ -82,7 +82,6 @@ class DllExport MGODEF : public EXTDEF { /* Table description */
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 */
...
...
storage/connect/plgxml.cpp
View file @
06af0367
...
...
@@ -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"
);
...
...
storage/connect/tabcmg.cpp
View file @
06af0367
...
...
@@ -26,6 +26,8 @@
#include "tabmul.h"
#include "filter.h"
PQRYRES
MGOColumns
(
PGLOBAL
g
,
PCSZ
db
,
PCSZ
uri
,
PTOS
topt
,
bool
info
);
/* -------------------------- Class CMGDISC -------------------------- */
/***********************************************************************/
...
...
storage/connect/tabdos.cpp
View file @
06af0367
/************* 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-20
19
*/
/* (C) Copyright to the author Olivier BERTRAND 1998-20
20
*/
/* */
/* 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
);
...
...
storage/connect/tabdos.h
View file @
06af0367
...
...
@@ -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
);
...
...
storage/connect/tabfix.h
View file @
06af0367
...
...
@@ -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__
storage/connect/tabjson.cpp
View file @
06af0367
...
...
@@ -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
;
...
...
storage/connect/tabjson.h
View file @
06af0367
...
...
@@ -104,7 +104,6 @@ class DllExport JSONDEF : public DOSDEF { /* Table description */
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 */
...
...
storage/connect/tabzip.cpp
View file @
06af0367
...
...
@@ -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
...
...
storage/connect/tabzip.h
View file @
06af0367
...
...
@@ -48,6 +48,8 @@ class DllExport TDBZIP : public TDBASE {
// 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
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment