Merge 10.0-connect -> 10.0
This commit is contained in:
commit
7dc48ae327
@ -5147,6 +5147,7 @@ char check_if_ignore_table(const char *table_name, char *table_type)
|
|||||||
if (!opt_no_data &&
|
if (!opt_no_data &&
|
||||||
(!my_strcasecmp(&my_charset_latin1, table_type, "MRG_MyISAM") ||
|
(!my_strcasecmp(&my_charset_latin1, table_type, "MRG_MyISAM") ||
|
||||||
!strcmp(table_type,"MRG_ISAM") ||
|
!strcmp(table_type,"MRG_ISAM") ||
|
||||||
|
!strcmp(table_type,"CONNECT") ||
|
||||||
!strcmp(table_type,"FEDERATED")))
|
!strcmp(table_type,"FEDERATED")))
|
||||||
result= IGNORE_DATA;
|
result= IGNORE_DATA;
|
||||||
}
|
}
|
||||||
|
@ -379,6 +379,7 @@ sub main {
|
|||||||
mtr_report("Logging: $0 ", join(" ", @ARGV));
|
mtr_report("Logging: $0 ", join(" ", @ARGV));
|
||||||
|
|
||||||
$DEFAULT_SUITES.= ',' . join(',', qw(
|
$DEFAULT_SUITES.= ',' . join(',', qw(
|
||||||
|
connect
|
||||||
query_response_time
|
query_response_time
|
||||||
sequence
|
sequence
|
||||||
spider
|
spider
|
||||||
|
@ -1117,3 +1117,95 @@
|
|||||||
fun:_nss_dns_gethostbyaddr_r
|
fun:_nss_dns_gethostbyaddr_r
|
||||||
fun:gethostbyaddr_r
|
fun:gethostbyaddr_r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:malloc
|
||||||
|
fun:strdup
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
fun:_ZN7ODBConn10GetDriversEP7_qryres
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:calloc
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
fun:_ZN7ODBConn10GetDriversEP7_qryres
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC SQLAllocEnv leavs some "still reachable" pointers
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:malloc
|
||||||
|
fun:strdup
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
fun:_ZN7ODBConn14GetDataSourcesEP7_qryres
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC SQLAllocEnv leavs some "still reachable" pointers
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:calloc
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
fun:_ZN7ODBConn14GetDataSourcesEP7_qryres
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:malloc
|
||||||
|
fun:strdup
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
fun:SQLDriverConnect
|
||||||
|
fun:_ZN7ODBConn7ConnectEj
|
||||||
|
fun:_ZN7ODBConn4OpenEPcj
|
||||||
|
fun:_Z11ODBCColumnsP7_globalPcS1_S1_b
|
||||||
|
fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:calloc
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
fun:SQLDriverConnect
|
||||||
|
fun:_ZN7ODBConn7ConnectEj
|
||||||
|
fun:_ZN7ODBConn4OpenEPcj
|
||||||
|
fun:_Z11ODBCColumnsP7_globalPcS1_S1_b
|
||||||
|
fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:malloc
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
fun:SQLDriverConnect
|
||||||
|
fun:_ZN7ODBConn7ConnectEj
|
||||||
|
fun:_ZN7ODBConn4OpenEPcj
|
||||||
|
fun:_Z11ODBCColumnsP7_globalPcS1_S1_b
|
||||||
|
fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ConnectSE: unixODBC dlopen leaves some "still reachable"
|
||||||
|
Memcheck:Leak
|
||||||
|
fun:malloc
|
||||||
|
fun:expand_dynamic_string_token
|
||||||
|
...
|
||||||
|
obj:*/libltdl.so*
|
||||||
|
...
|
||||||
|
obj:*/libodbc.so*
|
||||||
|
}
|
||||||
|
@ -178,7 +178,7 @@ bool COLBLK::InitValue(PGLOBAL g)
|
|||||||
(To_Tdb) ? To_Tdb->GetCat() : NULL)))
|
(To_Tdb) ? To_Tdb->GetCat() : NULL)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Status = BUF_READY;
|
AddStatus(BUF_READY);
|
||||||
Value->SetNullable(Nullable);
|
Value->SetNullable(Nullable);
|
||||||
|
|
||||||
#ifdef DEBTRACE
|
#ifdef DEBTRACE
|
||||||
|
@ -592,6 +592,18 @@ PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
|
|||||||
|
|
||||||
} // end of GetItem
|
} // end of GetItem
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
/* Reset the pointer on the deleted item. */
|
||||||
|
/******************************************************************/
|
||||||
|
bool DOMNODELIST::DropItem(PGLOBAL g, int n)
|
||||||
|
{
|
||||||
|
if (Listp == NULL || Listp->length <= n)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//Listp->item[n] = NULL; La propriété n'a pas de méthode 'set'
|
||||||
|
return false;
|
||||||
|
} // end of DeleteItem
|
||||||
|
|
||||||
/* ----------------------- class DOMATTR ------------------------ */
|
/* ----------------------- class DOMATTR ------------------------ */
|
||||||
|
|
||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
@ -618,8 +630,8 @@ bool DOMATTR::SetText(PGLOBAL g, char *txtp, int len)
|
|||||||
Len = len;
|
Len = len;
|
||||||
} // endif len
|
} // endif len
|
||||||
|
|
||||||
if (!MultiByteToWideChar(CP_ACP, 0, txtp, strlen(txtp) + 1,
|
if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1,
|
||||||
Ws, Len + 1)) {
|
Ws, Len + 1)) {
|
||||||
sprintf(g->Message, MSG(WS_CONV_ERR), txtp);
|
sprintf(g->Message, MSG(WS_CONV_ERR), txtp);
|
||||||
return true;
|
return true;
|
||||||
} // endif
|
} // endif
|
||||||
|
@ -104,8 +104,9 @@ class DOMNODELIST : public XMLNODELIST {
|
|||||||
friend class DOMNODE;
|
friend class DOMNODE;
|
||||||
public:
|
public:
|
||||||
// Methods
|
// Methods
|
||||||
virtual int GetLength(void) {return Listp->length;}
|
virtual int GetLength(void) {return Listp->length;}
|
||||||
virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
|
virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
|
||||||
|
virtual bool DropItem(PGLOBAL g, int n);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Constructor
|
// Constructor
|
||||||
|
@ -165,7 +165,7 @@ extern "C" char nmfile[];
|
|||||||
extern "C" char pdebug[];
|
extern "C" char pdebug[];
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
char version[]= "Version 1.01.0007 July 26, 2013";
|
char version[]= "Version 1.01.0008 August 18, 2013";
|
||||||
|
|
||||||
#if defined(XMSG)
|
#if defined(XMSG)
|
||||||
char msglang[]; // Default message language
|
char msglang[]; // Default message language
|
||||||
@ -1093,7 +1093,8 @@ PTDB ha_connect::GetTDB(PGLOBAL g)
|
|||||||
|
|
||||||
if (!xp->CheckQuery(valid_query_id) && tdbp
|
if (!xp->CheckQuery(valid_query_id) && tdbp
|
||||||
&& !stricmp(tdbp->GetName(), table_name)
|
&& !stricmp(tdbp->GetName(), table_name)
|
||||||
&& tdbp->GetMode() == xmod) {
|
&& (tdbp->GetMode() == xmod
|
||||||
|
|| tdbp->GetAmType() == TYPE_AM_XML)) {
|
||||||
tp= tdbp;
|
tp= tdbp;
|
||||||
tp->SetMode(xmod);
|
tp->SetMode(xmod);
|
||||||
} else if ((tp= CntGetTDB(g, table_name, xmod, this)))
|
} else if ((tp= CntGetTDB(g, table_name, xmod, this)))
|
||||||
@ -1132,13 +1133,14 @@ bool ha_connect::OpenTable(PGLOBAL g, bool del)
|
|||||||
break;
|
break;
|
||||||
} // endswitch xmode
|
} // endswitch xmode
|
||||||
|
|
||||||
if (xmod != MODE_INSERT) {
|
if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_ODBC
|
||||||
|
|| tdbp->GetAmType() == TYPE_AM_MYSQL) {
|
||||||
// Get the list of used fields (columns)
|
// Get the list of used fields (columns)
|
||||||
char *p;
|
char *p;
|
||||||
unsigned int k1, k2, n1, n2;
|
unsigned int k1, k2, n1, n2;
|
||||||
Field* *field;
|
Field* *field;
|
||||||
Field* fp;
|
Field* fp;
|
||||||
MY_BITMAP *map= table->read_set;
|
MY_BITMAP *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set;
|
||||||
MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
|
MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
|
||||||
|
|
||||||
k1= k2= 0;
|
k1= k2= 0;
|
||||||
@ -1373,7 +1375,8 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *buf)
|
|||||||
fp->option_struct->special)
|
fp->option_struct->special)
|
||||||
continue; // Is a virtual column possible here ???
|
continue; // Is a virtual column possible here ???
|
||||||
|
|
||||||
if (xmod == MODE_INSERT ||
|
if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
|
||||||
|
&& tdbp->GetAmType() != TYPE_AM_ODBC) ||
|
||||||
bitmap_is_set(table->write_set, fp->field_index)) {
|
bitmap_is_set(table->write_set, fp->field_index)) {
|
||||||
for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
|
for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
|
||||||
if (!stricmp(colp->GetName(), fp->field_name))
|
if (!stricmp(colp->GetName(), fp->field_name))
|
||||||
@ -2658,8 +2661,9 @@ int ha_connect::delete_all_rows()
|
|||||||
PGLOBAL g= xp->g;
|
PGLOBAL g= xp->g;
|
||||||
DBUG_ENTER("ha_connect::delete_all_rows");
|
DBUG_ENTER("ha_connect::delete_all_rows");
|
||||||
|
|
||||||
// Close and reopen the table so it will be deleted
|
if (tdbp && tdbp->GetAmType() != TYPE_AM_XML)
|
||||||
rc= CloseTable(g);
|
// Close and reopen the table so it will be deleted
|
||||||
|
rc= CloseTable(g);
|
||||||
|
|
||||||
if (!(OpenTable(g))) {
|
if (!(OpenTable(g))) {
|
||||||
if (CntDeleteRow(g, tdbp, true)) {
|
if (CntDeleteRow(g, tdbp, true)) {
|
||||||
@ -3462,7 +3466,7 @@ static bool add_field(String *sql, const char *field_name, int typ, int len,
|
|||||||
{
|
{
|
||||||
bool error= false;
|
bool error= false;
|
||||||
const char *type= PLGtoMYSQLtype(typ, dbf);
|
const char *type= PLGtoMYSQLtype(typ, dbf);
|
||||||
type= PLGtoMYSQLtype(typ, true);
|
// type= PLGtoMYSQLtype(typ, true); ?????
|
||||||
|
|
||||||
error|= sql->append('`');
|
error|= sql->append('`');
|
||||||
error|= sql->append(field_name);
|
error|= sql->append(field_name);
|
||||||
@ -3612,6 +3616,7 @@ static int init_table_share(THD *thd,
|
|||||||
static int init_table_share(THD* thd,
|
static int init_table_share(THD* thd,
|
||||||
TABLE_SHARE *table_s,
|
TABLE_SHARE *table_s,
|
||||||
HA_CREATE_INFO *create_info,
|
HA_CREATE_INFO *create_info,
|
||||||
|
// char *dsn,
|
||||||
String *sql)
|
String *sql)
|
||||||
{
|
{
|
||||||
bool oom= false;
|
bool oom= false;
|
||||||
@ -3670,10 +3675,12 @@ static int init_table_share(THD* thd,
|
|||||||
} // endfor opt
|
} // endfor opt
|
||||||
|
|
||||||
if (create_info->connect_string.length) {
|
if (create_info->connect_string.length) {
|
||||||
|
//if (dsn) {
|
||||||
oom|= sql->append(' ');
|
oom|= sql->append(' ');
|
||||||
oom|= sql->append("CONNECTION='");
|
oom|= sql->append("CONNECTION='");
|
||||||
oom|= sql->append_for_single_quote(create_info->connect_string.str,
|
oom|= sql->append_for_single_quote(create_info->connect_string.str,
|
||||||
create_info->connect_string.length);
|
create_info->connect_string.length);
|
||||||
|
// oom|= sql->append_for_single_quote(dsn, strlen(dsn));
|
||||||
oom|= sql->append('\'');
|
oom|= sql->append('\'');
|
||||||
|
|
||||||
if (oom)
|
if (oom)
|
||||||
@ -3715,6 +3722,25 @@ static void add_option(THD* thd, HA_CREATE_INFO *create_info,
|
|||||||
#endif // NEW_WAY
|
#endif // NEW_WAY
|
||||||
} // end of add_option
|
} // end of add_option
|
||||||
|
|
||||||
|
// Used to check whether a MYSQL table is created on itself
|
||||||
|
static bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
|
||||||
|
const char *db, char *tab, const char *src, int port)
|
||||||
|
{
|
||||||
|
if (src)
|
||||||
|
return false;
|
||||||
|
else if (host && stricmp(host, "localhost") && strcmp(host, "127.0.0.1"))
|
||||||
|
return false;
|
||||||
|
else if (db && stricmp(db, s->db.str))
|
||||||
|
return false;
|
||||||
|
else if (tab && stricmp(tab, s->table_name.str))
|
||||||
|
return false;
|
||||||
|
else if (port && port != (signed)GetDefaultPort())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
strcpy(g->Message, "This MySQL table is defined on itself");
|
||||||
|
return true;
|
||||||
|
} // end of CheckSelf
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief
|
@brief
|
||||||
connect_assisted_discovery() is called when creating a table with no columns.
|
connect_assisted_discovery() is called when creating a table with no columns.
|
||||||
@ -3733,13 +3759,13 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
{
|
{
|
||||||
char spc= ',', qch= 0;
|
char spc= ',', qch= 0;
|
||||||
const char *fncn= "?";
|
const char *fncn= "?";
|
||||||
const char *user, *fn, *db, *host, *pwd, *prt, *sep, *tbl, *src;
|
const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src;
|
||||||
const char *col, *ocl, *rnk, *pic, *fcl;
|
const char *col, *ocl, *rnk, *pic, *fcl;
|
||||||
char *tab, *dsn;
|
char *tab, *dsn;
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
char *nsp= NULL, *cls= NULL;
|
char *nsp= NULL, *cls= NULL;
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
int port= 0, hdr= 0, mxr= 0, rc= 0;
|
int port= 0, hdr= 0, mxr= 0, rc= 0, cop= 0;
|
||||||
uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
|
uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
|
||||||
bool bif, ok= false, dbf= false;
|
bool bif, ok= false, dbf= false;
|
||||||
TABTYPE ttp= TAB_UNDEF;
|
TABTYPE ttp= TAB_UNDEF;
|
||||||
@ -3760,7 +3786,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
if (!g)
|
if (!g)
|
||||||
return HA_ERR_INTERNAL_ERROR;
|
return HA_ERR_INTERNAL_ERROR;
|
||||||
|
|
||||||
user= host= pwd= prt= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL;
|
user= host= pwd= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL;
|
||||||
|
|
||||||
// Get the useful create options
|
// Get the useful create options
|
||||||
ttp= GetTypeID(topt->type);
|
ttp= GetTypeID(topt->type);
|
||||||
@ -3778,23 +3804,23 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
col= topt->colist;
|
col= topt->colist;
|
||||||
|
|
||||||
if (topt->oplist) {
|
if (topt->oplist) {
|
||||||
host= GetListOption(g,"host", topt->oplist, "localhost");
|
host= GetListOption(g, "host", topt->oplist, "localhost");
|
||||||
user= GetListOption(g,"user", topt->oplist, "root");
|
user= GetListOption(g, "user", topt->oplist, "root");
|
||||||
// Default value db can come from the DBNAME=xxx option.
|
// Default value db can come from the DBNAME=xxx option.
|
||||||
db= GetListOption(g,"database", topt->oplist, db);
|
db= GetListOption(g, "database", topt->oplist, db);
|
||||||
col= GetListOption(g,"colist", topt->oplist, col);
|
col= GetListOption(g, "colist", topt->oplist, col);
|
||||||
ocl= GetListOption(g,"occurcol", topt->oplist, NULL);
|
ocl= GetListOption(g, "occurcol", topt->oplist, NULL);
|
||||||
pic= GetListOption(g,"pivotcol", topt->oplist, NULL);
|
pic= GetListOption(g, "pivotcol", topt->oplist, NULL);
|
||||||
fcl= GetListOption(g,"fnccol", topt->oplist, NULL);
|
fcl= GetListOption(g, "fnccol", topt->oplist, NULL);
|
||||||
rnk= GetListOption(g,"rankcol", topt->oplist, NULL);
|
rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
|
||||||
pwd= GetListOption(g,"password", topt->oplist);
|
pwd= GetListOption(g, "password", topt->oplist);
|
||||||
prt= GetListOption(g,"port", topt->oplist);
|
|
||||||
port= (prt) ? atoi(prt) : 0;
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
nsp= GetListOption(g,"namespace", topt->oplist);
|
nsp= GetListOption(g, "namespace", topt->oplist);
|
||||||
cls= GetListOption(g,"class", topt->oplist);
|
cls= GetListOption(g, "class", topt->oplist);
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
port= atoi(GetListOption(g, "port", topt->oplist, "0"));
|
||||||
mxr= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
|
mxr= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
|
||||||
|
cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
|
||||||
} else {
|
} else {
|
||||||
host= "localhost";
|
host= "localhost";
|
||||||
user= "root";
|
user= "root";
|
||||||
@ -3840,7 +3866,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
} // endif p
|
} // endif p
|
||||||
|
|
||||||
} else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
|
} else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
|
||||||
tab= (char*)create_info->alias;
|
tab= table_s->table_name.str; // Default value
|
||||||
|
|
||||||
#if defined(NEW_WAY)
|
#if defined(NEW_WAY)
|
||||||
add_option(thd, create_info, "tabname", tab);
|
add_option(thd, create_info, "tabname", tab);
|
||||||
@ -3850,8 +3876,18 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
switch (ttp) {
|
switch (ttp) {
|
||||||
#if defined(ODBC_SUPPORT)
|
#if defined(ODBC_SUPPORT)
|
||||||
case TAB_ODBC:
|
case TAB_ODBC:
|
||||||
if (!(dsn= create_info->connect_string.str)
|
dsn= create_info->connect_string.str;
|
||||||
&& !(fnc & (FNC_DSN | FNC_DRIVER)))
|
|
||||||
|
if (fnc & (FNC_DSN | FNC_DRIVER))
|
||||||
|
ok= true;
|
||||||
|
else if (!stricmp(thd->main_security_ctx.host, "localhost")
|
||||||
|
&& cop == 1) {
|
||||||
|
if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) {
|
||||||
|
thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
|
||||||
|
ok= true;
|
||||||
|
} // endif dsn
|
||||||
|
|
||||||
|
} else if (!dsn)
|
||||||
sprintf(g->Message, "Missing %s connection string", topt->type);
|
sprintf(g->Message, "Missing %s connection string", topt->type);
|
||||||
else
|
else
|
||||||
ok= true;
|
ok= true;
|
||||||
@ -3873,31 +3909,46 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
case TAB_MYSQL:
|
case TAB_MYSQL:
|
||||||
ok= true;
|
ok= true;
|
||||||
|
|
||||||
if ((dsn= create_info->connect_string.str)) {
|
if (create_info->connect_string.str) {
|
||||||
|
int len= create_info->connect_string.length;
|
||||||
PMYDEF mydef= new(g) MYSQLDEF();
|
PMYDEF mydef= new(g) MYSQLDEF();
|
||||||
PDBUSER dup= PlgGetUser(g);
|
PDBUSER dup= PlgGetUser(g);
|
||||||
PCATLG cat= (dup) ? dup->Catalog : NULL;
|
PCATLG cat= (dup) ? dup->Catalog : NULL;
|
||||||
|
|
||||||
dsn= (char*)PlugSubAlloc(g, NULL, strlen(dsn) + 1);
|
dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
|
||||||
strncpy(dsn, create_info->connect_string.str,
|
strncpy(dsn, create_info->connect_string.str, len);
|
||||||
create_info->connect_string.length);
|
dsn[len]= 0;
|
||||||
dsn[create_info->connect_string.length]= 0;
|
|
||||||
mydef->SetName(create_info->alias);
|
mydef->SetName(create_info->alias);
|
||||||
mydef->SetCat(cat);
|
mydef->SetCat(cat);
|
||||||
|
|
||||||
if (!mydef->ParseURL(g, dsn)) {
|
if (!mydef->ParseURL(g, dsn, false)) {
|
||||||
host= mydef->GetHostname();
|
if (mydef->GetHostname())
|
||||||
user= mydef->GetUsername();
|
host= mydef->GetHostname();
|
||||||
pwd= mydef->GetPassword();
|
|
||||||
db= mydef->GetDatabase();
|
if (mydef->GetUsername())
|
||||||
tab= mydef->GetTabname();
|
user= mydef->GetUsername();
|
||||||
port= mydef->GetPortnumber();
|
|
||||||
|
if (mydef->GetPassword())
|
||||||
|
pwd= mydef->GetPassword();
|
||||||
|
|
||||||
|
if (mydef->GetDatabase())
|
||||||
|
db= mydef->GetDatabase();
|
||||||
|
|
||||||
|
if (mydef->GetTabname())
|
||||||
|
tab= mydef->GetTabname();
|
||||||
|
|
||||||
|
if (mydef->GetPortnumber())
|
||||||
|
port= mydef->GetPortnumber();
|
||||||
|
|
||||||
} else
|
} else
|
||||||
ok= false;
|
ok= false;
|
||||||
|
|
||||||
} else if (!user)
|
} else if (!user)
|
||||||
user= "root";
|
user= "root";
|
||||||
|
|
||||||
|
if (CheckSelf(g, table_s, host, db, tab, src, port))
|
||||||
|
ok= false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
#endif // MYSQL_SUPPORT
|
#endif // MYSQL_SUPPORT
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
@ -3946,7 +3997,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
else
|
else
|
||||||
return HA_ERR_INTERNAL_ERROR; // Should never happen
|
return HA_ERR_INTERNAL_ERROR; // Should never happen
|
||||||
|
|
||||||
if (src && ttp != TAB_PIVOT) {
|
if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC) {
|
||||||
qrp= SrcColumns(g, host, db, user, pwd, src, port);
|
qrp= SrcColumns(g, host, db, user, pwd, src, port);
|
||||||
|
|
||||||
if (qrp && ttp == TAB_OCCUR)
|
if (qrp && ttp == TAB_OCCUR)
|
||||||
@ -3964,7 +4015,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
switch (fnc) {
|
switch (fnc) {
|
||||||
case FNC_NO:
|
case FNC_NO:
|
||||||
case FNC_COL:
|
case FNC_COL:
|
||||||
qrp= ODBCColumns(g, dsn, (char *) tab, NULL, fnc == FNC_COL);
|
if (src) {
|
||||||
|
qrp= ODBCSrcCols(g, dsn, (char*)src);
|
||||||
|
src= NULL; // for next tests
|
||||||
|
} else
|
||||||
|
qrp= ODBCColumns(g, dsn, (char *) tab, NULL, fnc == FNC_COL);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case FNC_TABLE:
|
case FNC_TABLE:
|
||||||
qrp= ODBCTables(g, dsn, (char *) tab, true);
|
qrp= ODBCTables(g, dsn, (char *) tab, true);
|
||||||
@ -4130,6 +4186,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
|
|||||||
#else // !NEW_WAY
|
#else // !NEW_WAY
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc= init_table_share(thd, table_s, create_info, &sql);
|
rc= init_table_share(thd, table_s, create_info, &sql);
|
||||||
|
// rc= init_table_share(thd, table_s, create_info, dsn, &sql);
|
||||||
#endif // !NEW_WAY
|
#endif // !NEW_WAY
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@ -4250,6 +4307,54 @@ int ha_connect::create(const char *name, TABLE *table_arg,
|
|||||||
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
|
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
|
||||||
} // endif tabname
|
} // endif tabname
|
||||||
|
|
||||||
|
case TAB_MYSQL:
|
||||||
|
{const char *src= options->srcdef;
|
||||||
|
char *host, *db, *tab= (char*)options->tabname;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
host= GetListOption(g, "host", options->oplist, NULL);
|
||||||
|
db= GetListOption(g, "database", options->oplist, NULL);
|
||||||
|
port= atoi(GetListOption(g, "port", options->oplist, "0"));
|
||||||
|
|
||||||
|
if (create_info->connect_string.str) {
|
||||||
|
char *dsn;
|
||||||
|
int len= create_info->connect_string.length;
|
||||||
|
PMYDEF mydef= new(g) MYSQLDEF();
|
||||||
|
PDBUSER dup= PlgGetUser(g);
|
||||||
|
PCATLG cat= (dup) ? dup->Catalog : NULL;
|
||||||
|
|
||||||
|
dsn= (char*)PlugSubAlloc(g, NULL, len + 1);
|
||||||
|
strncpy(dsn, create_info->connect_string.str, len);
|
||||||
|
dsn[len]= 0;
|
||||||
|
mydef->SetName(create_info->alias);
|
||||||
|
mydef->SetCat(cat);
|
||||||
|
|
||||||
|
if (!mydef->ParseURL(g, dsn, false)) {
|
||||||
|
if (mydef->GetHostname())
|
||||||
|
host= mydef->GetHostname();
|
||||||
|
|
||||||
|
if (mydef->GetDatabase())
|
||||||
|
db= mydef->GetDatabase();
|
||||||
|
|
||||||
|
if (mydef->GetTabname())
|
||||||
|
tab= mydef->GetTabname();
|
||||||
|
|
||||||
|
if (mydef->GetPortnumber())
|
||||||
|
port= mydef->GetPortnumber();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
|
||||||
|
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
|
||||||
|
} // endif ParseURL
|
||||||
|
|
||||||
|
} // endif connect_string
|
||||||
|
|
||||||
|
if (CheckSelf(g, table_arg->s, host, db, tab, src, port)) {
|
||||||
|
my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
|
||||||
|
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
|
||||||
|
} // endif CheckSelf
|
||||||
|
|
||||||
|
}break;
|
||||||
default: /* do nothing */;
|
default: /* do nothing */;
|
||||||
break;
|
break;
|
||||||
} // endswitch ttp
|
} // endswitch ttp
|
||||||
|
@ -208,7 +208,8 @@ public:
|
|||||||
return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_HAS_RECORDS |
|
return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_HAS_RECORDS |
|
||||||
HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS |
|
HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS |
|
||||||
HA_NO_COPY_ON_ALTER | HA_CAN_VIRTUAL_COLUMNS |
|
HA_NO_COPY_ON_ALTER | HA_CAN_VIRTUAL_COLUMNS |
|
||||||
/*HA_NULL_IN_KEY |*/ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE);
|
HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
|
||||||
|
/*HA_NULL_IN_KEY |*/ HA_MUST_USE_TABLE_CONDITION_PUSHDOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief
|
/** @brief
|
||||||
|
@ -14,6 +14,10 @@
|
|||||||
#include "my_global.h"
|
#include "my_global.h"
|
||||||
//#endif // !WIN32
|
//#endif // !WIN32
|
||||||
|
|
||||||
|
#if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
|
||||||
|
#error "tree support not compiled in"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED)
|
#if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED)
|
||||||
#error "XPath not supported"
|
#error "XPath not supported"
|
||||||
#endif
|
#endif
|
||||||
@ -47,8 +51,6 @@ typedef struct _x2block { /* Loaded XML file block */
|
|||||||
short Type; /* TYPE_FB_XML */
|
short Type; /* TYPE_FB_XML */
|
||||||
int Retcode; /* Return code from Load */
|
int Retcode; /* Return code from Load */
|
||||||
xmlDocPtr Docp; /* Document interface pointer */
|
xmlDocPtr Docp; /* Document interface pointer */
|
||||||
// xmlXPathContextPtr Ctxp;
|
|
||||||
// xmlXPathObjectPtr Xop;
|
|
||||||
} X2BLOCK, *PX2BLOCK;
|
} X2BLOCK, *PX2BLOCK;
|
||||||
|
|
||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
@ -91,6 +93,8 @@ class LIBXMLDOC : public XMLDOCUMENT {
|
|||||||
xmlNodeSetPtr Nlist;
|
xmlNodeSetPtr Nlist;
|
||||||
xmlXPathContextPtr Ctxp;
|
xmlXPathContextPtr Ctxp;
|
||||||
xmlXPathObjectPtr Xop;
|
xmlXPathObjectPtr Xop;
|
||||||
|
xmlXPathObjectPtr NlXop;
|
||||||
|
xmlErrorPtr Xerr;
|
||||||
char *Buf; // Temporary
|
char *Buf; // Temporary
|
||||||
bool Nofreelist;
|
bool Nofreelist;
|
||||||
}; // end of class LIBXMLDOC
|
}; // end of class LIBXMLDOC
|
||||||
@ -141,6 +145,7 @@ class XML2NODELIST : public XMLNODELIST {
|
|||||||
// Methods
|
// Methods
|
||||||
virtual int GetLength(void);
|
virtual int GetLength(void);
|
||||||
virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
|
virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
|
||||||
|
virtual bool DropItem(PGLOBAL g, int n);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Constructor
|
// Constructor
|
||||||
@ -180,6 +185,23 @@ extern int trace;
|
|||||||
} // "C"
|
} // "C"
|
||||||
|
|
||||||
#if defined(MEMORY_TRACE)
|
#if defined(MEMORY_TRACE)
|
||||||
|
static int m = 0;
|
||||||
|
static char s[500];
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Tracing output function. */
|
||||||
|
/**************************************************************************/
|
||||||
|
void xtrc(char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start (ap, fmt);
|
||||||
|
|
||||||
|
//vfprintf(stderr, fmt, ap);
|
||||||
|
vsprintf(s, fmt, ap);
|
||||||
|
if (s[strlen(s)-1] == '\n')
|
||||||
|
s[strlen(s)-1] = 0;
|
||||||
|
va_end (ap);
|
||||||
|
} // end of htrc
|
||||||
|
|
||||||
static xmlFreeFunc Free;
|
static xmlFreeFunc Free;
|
||||||
static xmlMallocFunc Malloc;
|
static xmlMallocFunc Malloc;
|
||||||
static xmlMallocFunc MallocA;
|
static xmlMallocFunc MallocA;
|
||||||
@ -188,42 +210,53 @@ static xmlStrdupFunc Strdup;
|
|||||||
|
|
||||||
void xmlMyFree(void *mem)
|
void xmlMyFree(void *mem)
|
||||||
{
|
{
|
||||||
if (trace)
|
if (trace) {
|
||||||
htrc("Freeing at %p\n", mem);
|
htrc("%.4d Freeing at %p %s\n", ++m, mem, s);
|
||||||
|
*s = 0;
|
||||||
|
} // endif trace
|
||||||
Free(mem);
|
Free(mem);
|
||||||
} // end of xmlMyFree
|
} // end of xmlMyFree
|
||||||
|
|
||||||
void *xmlMyMalloc(size_t size)
|
void *xmlMyMalloc(size_t size)
|
||||||
{
|
{
|
||||||
void *p = Malloc(size);
|
void *p = Malloc(size);
|
||||||
if (trace)
|
if (trace) {
|
||||||
htrc("Allocating %.5d at %p\n", size, p);
|
htrc("%.4d Allocating %.5d at %p %s\n", ++m, size, p, s);
|
||||||
|
*s = 0;
|
||||||
|
} // endif trace
|
||||||
return p;
|
return p;
|
||||||
} // end of xmlMyMalloc
|
} // end of xmlMyMalloc
|
||||||
|
|
||||||
void *xmlMyMallocAtomic(size_t size)
|
void *xmlMyMallocAtomic(size_t size)
|
||||||
{
|
{
|
||||||
void *p = MallocA(size);
|
void *p = MallocA(size);
|
||||||
if (trace)
|
if (trace) {
|
||||||
htrc("Atom alloc %.5d at %p\n", size, p);
|
htrc("%.4d Atom alloc %.5d at %p %s\n", ++m, size, p, s);
|
||||||
|
*s = 0;
|
||||||
|
} // endif trace
|
||||||
return p;
|
return p;
|
||||||
} // end of xmlMyMallocAtomic
|
} // end of xmlMyMallocAtomic
|
||||||
|
|
||||||
void *xmlMyRealloc(void *mem, size_t size)
|
void *xmlMyRealloc(void *mem, size_t size)
|
||||||
{
|
{
|
||||||
void *p = Realloc(mem, size);
|
void *p = Realloc(mem, size);
|
||||||
if (trace)
|
if (trace) {
|
||||||
htrc("ReAlloc %.5d to %p from %p\n", size, p, mem);
|
htrc("%.4d ReAlloc %.5d to %p from %p %s\n", ++m, size, p, mem, s);
|
||||||
|
*s = 0;
|
||||||
|
} // endif trace
|
||||||
return p;
|
return p;
|
||||||
} // end of xmlMyRealloc
|
} // end of xmlMyRealloc
|
||||||
|
|
||||||
char *xmlMyStrdup(const char *str)
|
char *xmlMyStrdup(const char *str)
|
||||||
{
|
{
|
||||||
char *p = Strdup(str);
|
char *p = Strdup(str);
|
||||||
if (trace)
|
if (trace) {
|
||||||
htrc("Duplicating to %p from %p %s\n", p, str, str);
|
htrc("%.4d Duplicating to %p from %p %s %s\n", ++m, p, str, str, s);
|
||||||
|
*s = 0;
|
||||||
|
} // endif trace
|
||||||
return p;
|
return p;
|
||||||
} // end of xmlMyStrdup
|
} // end of xmlMyStrdup
|
||||||
|
#define htrc xtrc
|
||||||
#endif // MEMORY_TRACE
|
#endif // MEMORY_TRACE
|
||||||
|
|
||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
@ -295,6 +328,8 @@ LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp)
|
|||||||
Nlist = NULL;
|
Nlist = NULL;
|
||||||
Ctxp = NULL;
|
Ctxp = NULL;
|
||||||
Xop = NULL;
|
Xop = NULL;
|
||||||
|
NlXop = NULL;
|
||||||
|
Xerr = NULL;
|
||||||
Buf = NULL;
|
Buf = NULL;
|
||||||
Nofreelist = false;
|
Nofreelist = false;
|
||||||
} // end of LIBXMLDOC constructor
|
} // end of LIBXMLDOC constructor
|
||||||
@ -321,9 +356,10 @@ bool LIBXMLDOC::ParseFile(char *fn)
|
|||||||
Encoding = (char*)Docp->encoding;
|
Encoding = (char*)Docp->encoding;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else if ((Xerr = xmlGetLastError()))
|
||||||
return true;
|
xmlResetError(Xerr);
|
||||||
|
|
||||||
|
return true;
|
||||||
} // end of ParseFile
|
} // end of ParseFile
|
||||||
|
|
||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
@ -344,8 +380,6 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
|
|||||||
xp->Length = (m == MODE_READ) ? 1 : 0;
|
xp->Length = (m == MODE_READ) ? 1 : 0;
|
||||||
xp->Retcode = rc;
|
xp->Retcode = rc;
|
||||||
xp->Docp = Docp;
|
xp->Docp = Docp;
|
||||||
// xp->Ctxp = Ctxp;
|
|
||||||
// xp->Xop = Xop;
|
|
||||||
|
|
||||||
// Return xp as a fp
|
// Return xp as a fp
|
||||||
return (PFBLOCK)xp;
|
return (PFBLOCK)xp;
|
||||||
@ -356,6 +390,9 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
|
|||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
bool LIBXMLDOC::NewDoc(PGLOBAL g, char *ver)
|
bool LIBXMLDOC::NewDoc(PGLOBAL g, char *ver)
|
||||||
{
|
{
|
||||||
|
if (trace)
|
||||||
|
htrc("NewDoc\n");
|
||||||
|
|
||||||
return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL);
|
return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL);
|
||||||
} // end of NewDoc
|
} // end of NewDoc
|
||||||
|
|
||||||
@ -462,8 +499,7 @@ int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn)
|
|||||||
if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) {
|
if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) {
|
||||||
xmlErrorPtr err = xmlGetLastError();
|
xmlErrorPtr err = xmlGetLastError();
|
||||||
|
|
||||||
strcpy(g->Message, (err) ? err->message : "Error saving XML doc"
|
strcpy(g->Message, (err) ? err->message : "Error saving XML doc");
|
||||||
);
|
|
||||||
rc = -1;
|
rc = -1;
|
||||||
} // endif Save
|
} // endif Save
|
||||||
// rc = xmlDocDump(of, Docp);
|
// rc = xmlDocDump(of, Docp);
|
||||||
@ -497,17 +533,44 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp)
|
|||||||
if (trace)
|
if (trace)
|
||||||
htrc("CloseDoc: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0);
|
htrc("CloseDoc: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0);
|
||||||
|
|
||||||
if (xp && xp->Count == 1) {
|
//if (xp && xp->Count == 1) {
|
||||||
if (Nlist)
|
if (Nlist) {
|
||||||
xmlXPathFreeNodeSet(Nlist);
|
xmlXPathFreeNodeSet(Nlist);
|
||||||
|
|
||||||
if (Xop)
|
if ((Xerr = xmlGetLastError()))
|
||||||
|
xmlResetError(Xerr);
|
||||||
|
|
||||||
|
Nlist = NULL;
|
||||||
|
} // endif Nlist
|
||||||
|
|
||||||
|
if (Xop) {
|
||||||
xmlXPathFreeObject(Xop);
|
xmlXPathFreeObject(Xop);
|
||||||
|
|
||||||
if (Ctxp)
|
if ((Xerr = xmlGetLastError()))
|
||||||
|
xmlResetError(Xerr);
|
||||||
|
|
||||||
|
Xop = NULL;
|
||||||
|
} // endif Xop
|
||||||
|
|
||||||
|
if (NlXop) {
|
||||||
|
xmlXPathFreeObject(NlXop);
|
||||||
|
|
||||||
|
if ((Xerr = xmlGetLastError()))
|
||||||
|
xmlResetError(Xerr);
|
||||||
|
|
||||||
|
NlXop = NULL;
|
||||||
|
} // endif NlXop
|
||||||
|
|
||||||
|
if (Ctxp) {
|
||||||
xmlXPathFreeContext(Ctxp);
|
xmlXPathFreeContext(Ctxp);
|
||||||
|
|
||||||
} // endif Count
|
if ((Xerr = xmlGetLastError()))
|
||||||
|
xmlResetError(Xerr);
|
||||||
|
|
||||||
|
Ctxp = NULL;
|
||||||
|
} // endif Ctxp
|
||||||
|
|
||||||
|
// } // endif Count
|
||||||
|
|
||||||
CloseXML2File(g, xp, false);
|
CloseXML2File(g, xp, false);
|
||||||
} // end of Close
|
} // end of Close
|
||||||
@ -560,18 +623,29 @@ xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp)
|
|||||||
|
|
||||||
} // endfor nsp
|
} // endfor nsp
|
||||||
|
|
||||||
} else {
|
} // endif Ctxp
|
||||||
|
|
||||||
|
if (Xop) {
|
||||||
if (trace)
|
if (trace)
|
||||||
htrc("Calling xmlXPathFreeNodeSetList Xop=%p\n", Xop);
|
htrc("Calling xmlXPathFreeNodeSetList Xop=%p NOFREE=%d\n",
|
||||||
|
Xop, Nofreelist);
|
||||||
|
|
||||||
if (Nofreelist) {
|
if (Nofreelist) {
|
||||||
// Making Nlist that must not be freed yet
|
// Making Nlist that must not be freed yet
|
||||||
xmlXPathFreeNodeSetList(Xop); // Caused memory leak
|
// xmlXPathFreeNodeSetList(Xop); // Caused memory leak
|
||||||
|
assert(!NlXop);
|
||||||
|
NlXop = Xop; // Freed on closing
|
||||||
Nofreelist = false;
|
Nofreelist = false;
|
||||||
} else
|
} else
|
||||||
xmlXPathFreeObject(Xop); // Caused node not found
|
xmlXPathFreeObject(Xop); // Caused node not found
|
||||||
|
|
||||||
} // endif Ctxp
|
if ((Xerr = xmlGetLastError())) {
|
||||||
|
strcpy(g->Message, Xerr->message);
|
||||||
|
xmlResetError(Xerr);
|
||||||
|
return NULL;
|
||||||
|
} // endif Xerr
|
||||||
|
|
||||||
|
} // endif Xop
|
||||||
|
|
||||||
// Set the context to the calling node
|
// Set the context to the calling node
|
||||||
Ctxp->node = np;
|
Ctxp->node = np;
|
||||||
@ -990,6 +1064,8 @@ void XML2NODE::AddText(PGLOBAL g, char *txtp)
|
|||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp)
|
void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp)
|
||||||
{
|
{
|
||||||
|
xmlErrorPtr xerr;
|
||||||
|
|
||||||
if (trace)
|
if (trace)
|
||||||
htrc("DeleteChild: node=%p\n", dnp);
|
htrc("DeleteChild: node=%p\n", dnp);
|
||||||
|
|
||||||
@ -999,12 +1075,39 @@ void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp)
|
|||||||
// This is specific to row nodes
|
// This is specific to row nodes
|
||||||
if (text && text->type == XML_TEXT_NODE) {
|
if (text && text->type == XML_TEXT_NODE) {
|
||||||
xmlUnlinkNode(text);
|
xmlUnlinkNode(text);
|
||||||
|
|
||||||
|
if ((xerr = xmlGetLastError()))
|
||||||
|
goto err;
|
||||||
|
|
||||||
xmlFreeNode(text);
|
xmlFreeNode(text);
|
||||||
|
|
||||||
|
if ((xerr = xmlGetLastError()))
|
||||||
|
goto err;
|
||||||
|
|
||||||
} // endif type
|
} // endif type
|
||||||
|
|
||||||
xmlUnlinkNode(np);
|
xmlUnlinkNode(np);
|
||||||
|
|
||||||
|
if ((xerr = xmlGetLastError()))
|
||||||
|
goto err;
|
||||||
|
|
||||||
xmlFreeNode(np);
|
xmlFreeNode(np);
|
||||||
|
|
||||||
|
if ((xerr = xmlGetLastError()))
|
||||||
|
goto err;
|
||||||
|
|
||||||
Delete(dnp);
|
Delete(dnp);
|
||||||
|
|
||||||
|
if ((xerr = xmlGetLastError()))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (trace)
|
||||||
|
htrc("DeleteChild: errmsg=%s\n", xerr->message);
|
||||||
|
|
||||||
|
xmlResetError(xerr);
|
||||||
} // end of DeleteChild
|
} // end of DeleteChild
|
||||||
|
|
||||||
/* -------------------- class XML2NODELIST ---------------------- */
|
/* -------------------- class XML2NODELIST ---------------------- */
|
||||||
@ -1045,6 +1148,22 @@ PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
|
|||||||
|
|
||||||
} // end of GetItem
|
} // end of GetItem
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
/* Reset the pointer on the deleted item. */
|
||||||
|
/******************************************************************/
|
||||||
|
bool XML2NODELIST::DropItem(PGLOBAL g, int n)
|
||||||
|
{
|
||||||
|
if (trace)
|
||||||
|
htrc("DropItem: n=%d\n", n);
|
||||||
|
|
||||||
|
// We should do something here
|
||||||
|
if (!Listp || Listp->nodeNr <= n)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Listp->nodeTab[n] = NULL; // This was causing Valgrind warning
|
||||||
|
return false;
|
||||||
|
} // end of DropItem
|
||||||
|
|
||||||
/* ---------------------- class XML2ATTR ------------------------ */
|
/* ---------------------- class XML2ATTR ------------------------ */
|
||||||
|
|
||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
|
@ -89,7 +89,7 @@ PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db,
|
|||||||
static unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 256, 32, 32};
|
static unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 256, 32, 32};
|
||||||
char *fld, *fmt, cmd[128];
|
char *fld, *fmt, cmd[128];
|
||||||
int i, n, nf, ncol = sizeof(buftyp) / sizeof(int);
|
int i, n, nf, ncol = sizeof(buftyp) / sizeof(int);
|
||||||
int len, type, prec, rc, k = 0;
|
int len, type, prec, rc, k = 0;
|
||||||
PQRYRES qrp;
|
PQRYRES qrp;
|
||||||
PCOLRES crp;
|
PCOLRES crp;
|
||||||
MYSQLC myc;
|
MYSQLC myc;
|
||||||
@ -166,7 +166,7 @@ PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db,
|
|||||||
// Get type, type name, and precision
|
// Get type, type name, and precision
|
||||||
fld = myc.GetCharField(1);
|
fld = myc.GetCharField(1);
|
||||||
prec = 0;
|
prec = 0;
|
||||||
len = 256; // Default for text or blob
|
len = 255; // Default for text or blob
|
||||||
|
|
||||||
if ((nf = sscanf(fld, "%[^(](%d,%d", cmd, &len, &prec)) < 1) {
|
if ((nf = sscanf(fld, "%[^(](%d,%d", cmd, &len, &prec)) < 1) {
|
||||||
sprintf(g->Message, MSG(BAD_FIELD_TYPE), fld);
|
sprintf(g->Message, MSG(BAD_FIELD_TYPE), fld);
|
||||||
|
@ -54,6 +54,7 @@ uint GetDefaultPort(void);
|
|||||||
class DllItem MYSQLC {
|
class DllItem MYSQLC {
|
||||||
friend class TDBMYSQL;
|
friend class TDBMYSQL;
|
||||||
friend class MYSQLCOL;
|
friend class MYSQLCOL;
|
||||||
|
friend class TDBMYEXC;
|
||||||
// Construction
|
// Construction
|
||||||
public:
|
public:
|
||||||
MYSQLC(void);
|
MYSQLC(void);
|
||||||
|
@ -217,3 +217,24 @@ t2 CREATE TABLE `t2` (
|
|||||||
SELECT * FROM t2;
|
SELECT * FROM t2;
|
||||||
a
|
a
|
||||||
DROP TABLE t2, t1;
|
DROP TABLE t2, t1;
|
||||||
|
#
|
||||||
|
# MDEV-4877 mysqldump dumps all data from a connect table
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
INSERT INTO t1 VALUES (10),(20),(30);
|
||||||
|
CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL CONNECTION='mysql://root@localhost:PORT/test/t1';
|
||||||
|
SELECT * FROM t2;
|
||||||
|
a
|
||||||
|
10
|
||||||
|
20
|
||||||
|
30
|
||||||
|
# Start of mysqldump ------
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `t2` (
|
||||||
|
`a` int(11) DEFAULT NULL
|
||||||
|
) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='mysql://root@localhost:PORT/test/t1' `TABLE_TYPE`='MYSQL';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
# End of mysqldump ------
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -413,7 +413,7 @@ DROP TABLE t1;
|
|||||||
SET @a=LOAD_FILE('MYSQLD_DATADIR/test/t1.xml');
|
SET @a=LOAD_FILE('MYSQLD_DATADIR/test/t1.xml');
|
||||||
SELECT CAST(@a AS CHAR CHARACTER SET latin1);
|
SELECT CAST(@a AS CHAR CHARACTER SET latin1);
|
||||||
CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?>
|
CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
<!-- Created by CONNECT Version 1.01.0007 July 26, 2013 -->
|
<!-- Created by CONNECT Version 1.01.0008 August 18, 2013 -->
|
||||||
<t1>
|
<t1>
|
||||||
<line>
|
<line>
|
||||||
<node>ÀÁÂÃ</node>
|
<node>ÀÁÂÃ</node>
|
||||||
|
@ -10,7 +10,7 @@ let $PORT= `select @@port`;
|
|||||||
--disable_query_log
|
--disable_query_log
|
||||||
--replace_result $PORT PORT
|
--replace_result $PORT PORT
|
||||||
--error 0,ER_UNKNOWN_ERROR
|
--error 0,ER_UNKNOWN_ERROR
|
||||||
--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT'
|
--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='tx1' OPTION_LIST='host=localhost,user=root,port=$PORT'
|
||||||
if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
|
if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
|
||||||
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
|
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
|
||||||
AND ENGINE='CONNECT'
|
AND ENGINE='CONNECT'
|
||||||
@ -420,3 +420,17 @@ DROP TABLE t2, t1;
|
|||||||
# TODO: add test for YEAR
|
# TODO: add test for YEAR
|
||||||
# TODO: add tests for fractional seconds
|
# TODO: add tests for fractional seconds
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-4877 mysqldump dumps all data from a connect table
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
INSERT INTO t1 VALUES (10),(20),(30);
|
||||||
|
--replace_result $PORT PORT
|
||||||
|
--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL CONNECTION='mysql://root@localhost:$PORT/test/t1'
|
||||||
|
SELECT * FROM t2;
|
||||||
|
--echo # Start of mysqldump ------
|
||||||
|
--replace_result $PORT PORT
|
||||||
|
--exec $MYSQL_DUMP --compact test t2
|
||||||
|
--echo # End of mysqldump ------
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -1,78 +1,78 @@
|
|||||||
-- source include/not_embedded.inc
|
-- source include/not_embedded.inc
|
||||||
|
|
||||||
let $PORT= `select @@port`;
|
let $PORT= `select @@port`;
|
||||||
|
|
||||||
--disable_query_log
|
--disable_query_log
|
||||||
--replace_result $PORT PORT
|
--replace_result $PORT PORT
|
||||||
--error 0,ER_UNKNOWN_ERROR
|
--error 0,ER_UNKNOWN_ERROR
|
||||||
--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT'
|
--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='tx1' OPTION_LIST='host=localhost,user=root,port=$PORT'
|
||||||
if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
|
if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
|
||||||
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
|
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
|
||||||
AND ENGINE='CONNECT'
|
AND ENGINE='CONNECT'
|
||||||
AND CREATE_OPTIONS LIKE '%`table_type`=MySQL%'`)
|
AND CREATE_OPTIONS LIKE '%`table_type`=MySQL%'`)
|
||||||
{
|
{
|
||||||
Skip Need MySQL support;
|
Skip Need MySQL support;
|
||||||
}
|
}
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
--enable_query_log
|
--enable_query_log
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # Testing FILE privilege
|
--echo # Testing FILE privilege
|
||||||
--echo #
|
--echo #
|
||||||
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
|
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
|
||||||
REVOKE FILE ON *.* FROM user@localhost;
|
REVOKE FILE ON *.* FROM user@localhost;
|
||||||
--connect(user,localhost,user,,)
|
--connect(user,localhost,user,,)
|
||||||
--connection user
|
--connection user
|
||||||
SELECT user();
|
SELECT user();
|
||||||
--replace_result $PORT PORT
|
--replace_result $PORT PORT
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT'
|
--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT'
|
||||||
--connection default
|
--connection default
|
||||||
SELECT user();
|
SELECT user();
|
||||||
CREATE TABLE t1remote (a INT NOT NULL);
|
CREATE TABLE t1remote (a INT NOT NULL);
|
||||||
INSERT INTO t1remote VALUES (10),(20),(30);
|
INSERT INTO t1remote VALUES (10),(20),(30);
|
||||||
--replace_result $PORT PORT
|
--replace_result $PORT PORT
|
||||||
--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT'
|
--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT'
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
--connection user
|
--connection user
|
||||||
SELECT user();
|
SELECT user();
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
INSERT INTO t1 VALUES ('xxx');
|
INSERT INTO t1 VALUES ('xxx');
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
DELETE FROM t1 WHERE a='xxx';
|
DELETE FROM t1 WHERE a='xxx';
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
UPDATE t1 SET a='yyy' WHERE a='xxx';
|
UPDATE t1 SET a='yyy' WHERE a='xxx';
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
TRUNCATE TABLE t1;
|
TRUNCATE TABLE t1;
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
ALTER TABLE t1 READONLY=1;
|
ALTER TABLE t1 READONLY=1;
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||||
|
|
||||||
--echo # Testing a VIEW created with FILE privileges but accessed with no FILE
|
--echo # Testing a VIEW created with FILE privileges but accessed with no FILE
|
||||||
--connection default
|
--connection default
|
||||||
SELECT user();
|
SELECT user();
|
||||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||||
--connection user
|
--connection user
|
||||||
SELECT user();
|
SELECT user();
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
SELECT * FROM v1;
|
SELECT * FROM v1;
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
INSERT INTO v1 VALUES (2);
|
INSERT INTO v1 VALUES (2);
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
UPDATE v1 SET a=123;
|
UPDATE v1 SET a=123;
|
||||||
--error ER_ACCESS_DENIED_ERROR
|
--error ER_ACCESS_DENIED_ERROR
|
||||||
DELETE FROM v1;
|
DELETE FROM v1;
|
||||||
|
|
||||||
--disconnect user
|
--disconnect user
|
||||||
--connection default
|
--connection default
|
||||||
SELECT user();
|
SELECT user();
|
||||||
DROP VIEW v1;
|
DROP VIEW v1;
|
||||||
DROP TABLE t1, t1remote;
|
DROP TABLE t1, t1remote;
|
||||||
DROP USER user@localhost;
|
DROP USER user@localhost;
|
||||||
--echo #
|
--echo #
|
||||||
--echo # Testing FILE privileges done
|
--echo # Testing FILE privileges done
|
||||||
--echo #
|
--echo #
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* ODBC catalog function prototypes. */
|
/* ODBC catalog function prototypes. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop);
|
||||||
PQRYRES ODBCDataSources(PGLOBAL g, bool info);
|
PQRYRES ODBCDataSources(PGLOBAL g, bool info);
|
||||||
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
|
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
|
||||||
char *colpat, bool info);
|
char *colpat, bool info);
|
||||||
|
PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src);
|
||||||
PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info);
|
PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info);
|
||||||
PQRYRES ODBCDrivers(PGLOBAL g, bool info);
|
PQRYRES ODBCDrivers(PGLOBAL g, bool info);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/************ Odbconn C++ Functions Source Code File (.CPP) ************/
|
/************ Odbconn C++ Functions Source Code File (.CPP) ************/
|
||||||
/* Name: ODBCONN.CPP Version 1.6 */
|
/* Name: ODBCONN.CPP Version 1.7 */
|
||||||
/* */
|
/* */
|
||||||
/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */
|
/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */
|
||||||
/* */
|
/* */
|
||||||
@ -13,6 +13,7 @@
|
|||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
//nclude <io.h>
|
//nclude <io.h>
|
||||||
//nclude <fcntl.h>
|
//nclude <fcntl.h>
|
||||||
|
#include <direct.h> // for getcwd
|
||||||
#if defined(__BORLANDC__)
|
#if defined(__BORLANDC__)
|
||||||
#define __MFC_COMPAT__ // To define min/max as macro
|
#define __MFC_COMPAT__ // To define min/max as macro
|
||||||
#endif
|
#endif
|
||||||
@ -76,8 +77,9 @@ static short GetSQLType(int type)
|
|||||||
case TYPE_SHORT: tp = SQL_SMALLINT; break;
|
case TYPE_SHORT: tp = SQL_SMALLINT; break;
|
||||||
case TYPE_INT: tp = SQL_INTEGER; break;
|
case TYPE_INT: tp = SQL_INTEGER; break;
|
||||||
case TYPE_DATE: tp = SQL_TIMESTAMP; break;
|
case TYPE_DATE: tp = SQL_TIMESTAMP; break;
|
||||||
case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5)
|
case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5)
|
||||||
case TYPE_FLOAT: tp = SQL_DOUBLE; break;
|
case TYPE_FLOAT: tp = SQL_DOUBLE; break;
|
||||||
|
case TYPE_TINY : tp = SQL_TINYINT; break;
|
||||||
} // endswitch type
|
} // endswitch type
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
@ -97,6 +99,7 @@ static int GetSQLCType(int type)
|
|||||||
case TYPE_DATE: tp = SQL_C_TIMESTAMP; break;
|
case TYPE_DATE: tp = SQL_C_TIMESTAMP; break;
|
||||||
case TYPE_BIGINT: tp = SQL_C_SBIGINT; break;
|
case TYPE_BIGINT: tp = SQL_C_SBIGINT; break;
|
||||||
case TYPE_FLOAT: tp = SQL_C_DOUBLE; break;
|
case TYPE_FLOAT: tp = SQL_C_DOUBLE; break;
|
||||||
|
case TYPE_TINY : tp = SQL_C_TINYINT; break;
|
||||||
} // endswitch type
|
} // endswitch type
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
@ -127,9 +130,11 @@ int TranslateSQLType(int stp, int prec, int& len)
|
|||||||
type = TYPE_INT;
|
type = TYPE_INT;
|
||||||
break;
|
break;
|
||||||
case SQL_SMALLINT: // 5
|
case SQL_SMALLINT: // 5
|
||||||
|
type = TYPE_SHORT;
|
||||||
|
break;
|
||||||
case SQL_TINYINT: // (-6)
|
case SQL_TINYINT: // (-6)
|
||||||
case SQL_BIT: // (-7)
|
case SQL_BIT: // (-7)
|
||||||
type = TYPE_SHORT;
|
type = TYPE_TINY;
|
||||||
break;
|
break;
|
||||||
case SQL_FLOAT: // 6
|
case SQL_FLOAT: // 6
|
||||||
case SQL_REAL: // 7
|
case SQL_REAL: // 7
|
||||||
@ -168,10 +173,36 @@ int TranslateSQLType(int stp, int prec, int& len)
|
|||||||
} // end of TranslateSQLType
|
} // end of TranslateSQLType
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* ODBConn static members initialization. */
|
/* ODBCCheckConnection: Check completeness of connection string. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
//HENV ODBConn::m_henv = SQL_NULL_HENV;
|
char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop)
|
||||||
//int ODBConn::m_nAlloc = 0; // per-Appl reference to HENV above
|
{
|
||||||
|
char *newdsn, dir[_MAX_PATH], buf[_MAX_PATH];
|
||||||
|
int rc;
|
||||||
|
DWORD options = ODBConn::openReadOnly;
|
||||||
|
ODBConn *ocp = new(g) ODBConn(g, NULL);
|
||||||
|
|
||||||
|
(void) getcwd(dir, sizeof(dir) - 1);
|
||||||
|
|
||||||
|
switch (cop) {
|
||||||
|
case 1: options |= ODBConn::forceOdbcDialog; break;
|
||||||
|
case 2: options |= ODBConn::noOdbcDialog; break;
|
||||||
|
} // endswitch cop
|
||||||
|
|
||||||
|
if (ocp->Open(dsn, options) < 1)
|
||||||
|
newdsn = NULL;
|
||||||
|
else
|
||||||
|
newdsn = ocp->GetConnect();
|
||||||
|
|
||||||
|
(void) getcwd(buf, sizeof(buf) - 1);
|
||||||
|
|
||||||
|
// Some data sources change the current directory
|
||||||
|
if (strcmp(dir, buf))
|
||||||
|
rc = chdir(dir);
|
||||||
|
|
||||||
|
ocp->Close();
|
||||||
|
return newdsn; // Return complete connection string
|
||||||
|
} // end of ODBCCheckConnection
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* Allocate the structure used to refer to the result set. */
|
/* Allocate the structure used to refer to the result set. */
|
||||||
@ -225,7 +256,6 @@ static void ResetNullValues(CATPARM *cap)
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* ODBCColumns: constructs the result blocks containing all columns */
|
/* ODBCColumns: constructs the result blocks containing all columns */
|
||||||
/* of an ODBC table that will be retrieved by GetData commands. */
|
/* of an ODBC table that will be retrieved by GetData commands. */
|
||||||
/* Note: The first two columns (Qualifier, Owner) are ignored. */
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
|
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
|
||||||
char *colpat, bool info)
|
char *colpat, bool info)
|
||||||
@ -251,7 +281,7 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
|
|||||||
if (!info) {
|
if (!info) {
|
||||||
ocp = new(g) ODBConn(g, NULL);
|
ocp = new(g) ODBConn(g, NULL);
|
||||||
|
|
||||||
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
|
if (ocp->Open(dsn, 10) < 1) // openReadOnly + noODBCdialog
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// We fix a MySQL limit because some data sources return 32767
|
// We fix a MySQL limit because some data sources return 32767
|
||||||
@ -314,6 +344,17 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table,
|
|||||||
return qrp;
|
return qrp;
|
||||||
} // end of ODBCColumns
|
} // end of ODBCColumns
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/* ODBCSrcCols: constructs the result blocks containing the */
|
||||||
|
/* description of all the columns of a Srcdef option. */
|
||||||
|
/**************************************************************************/
|
||||||
|
PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src)
|
||||||
|
{
|
||||||
|
ODBConn *ocp = new(g) ODBConn(g, NULL);
|
||||||
|
|
||||||
|
return ocp->GetMetaData(g, dsn, src);
|
||||||
|
} // end of ODBCSrcCols
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
/* MyODBCCols: returns column info as required by ha_connect::pre_create. */
|
/* MyODBCCols: returns column info as required by ha_connect::pre_create. */
|
||||||
@ -800,6 +841,17 @@ void DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
|
|||||||
|
|
||||||
} // end of BuildErrorMessage
|
} // end of BuildErrorMessage
|
||||||
|
|
||||||
|
const char *DBX::GetErrorMessage(int i)
|
||||||
|
{
|
||||||
|
if (i < 0 || i >= MAX_NUM_OF_MSG)
|
||||||
|
return "No ODBC error";
|
||||||
|
else if (m_ErrMsg[i])
|
||||||
|
return m_ErrMsg[i];
|
||||||
|
else
|
||||||
|
return (m_Msg) ? m_Msg : "Unknown error";
|
||||||
|
|
||||||
|
} // end of GetErrorMessage
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* ODBConn construction/destruction. */
|
/* ODBConn construction/destruction. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -818,7 +870,7 @@ ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp)
|
|||||||
m_Catver = (tdbp) ? tdbp->Catver : 0;
|
m_Catver = (tdbp) ? tdbp->Catver : 0;
|
||||||
m_Connect = NULL;
|
m_Connect = NULL;
|
||||||
m_Updatable = true;
|
m_Updatable = true;
|
||||||
//m_Transactions = false;
|
m_Transact = false;
|
||||||
m_IDQuoteChar = '\'';
|
m_IDQuoteChar = '\'';
|
||||||
//*m_ErrMsg = '\0';
|
//*m_ErrMsg = '\0';
|
||||||
} // end of ODBConn
|
} // end of ODBConn
|
||||||
@ -1061,7 +1113,7 @@ bool ODBConn::Connect(DWORD Options)
|
|||||||
if (hWnd == NULL)
|
if (hWnd == NULL)
|
||||||
hWnd = GetDesktopWindow();
|
hWnd = GetDesktopWindow();
|
||||||
#else // !WIN32
|
#else // !WIN32
|
||||||
HWND hWnd = NULL;
|
HWND hWnd = 1;
|
||||||
#endif // !WIN32
|
#endif // !WIN32
|
||||||
PGLOBAL& g = m_G;
|
PGLOBAL& g = m_G;
|
||||||
PDBUSER dup = PlgGetUser(g);
|
PDBUSER dup = PlgGetUser(g);
|
||||||
@ -1204,8 +1256,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
|
|||||||
void *buffer;
|
void *buffer;
|
||||||
bool b;
|
bool b;
|
||||||
UWORD n;
|
UWORD n;
|
||||||
SWORD ncol, len, tp;
|
SWORD len, tp, ncol = 0;
|
||||||
SQLLEN afrw;
|
|
||||||
ODBCCOL *colp;
|
ODBCCOL *colp;
|
||||||
RETCODE rc;
|
RETCODE rc;
|
||||||
HSTMT hstmt;
|
HSTMT hstmt;
|
||||||
@ -1240,26 +1291,44 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
|
|||||||
if (trace)
|
if (trace)
|
||||||
htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql);
|
htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql);
|
||||||
|
|
||||||
do {
|
if (m_Tdb->Srcdef) {
|
||||||
rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
|
// Be sure this is a query returning a result set
|
||||||
} while (rc == SQL_STILL_EXECUTING);
|
do {
|
||||||
|
rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
|
||||||
if (!Check(rc))
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
ThrowDBX(rc, "SQLExecDirect", hstmt);
|
|
||||||
|
|
||||||
do {
|
|
||||||
rc = SQLNumResultCols(hstmt, &ncol);
|
|
||||||
} while (rc == SQL_STILL_EXECUTING);
|
|
||||||
|
|
||||||
if (ncol == 0) {
|
|
||||||
// Update or Delete statement
|
|
||||||
rc = SQLRowCount(hstmt, &afrw);
|
|
||||||
|
|
||||||
if (!Check(rc))
|
if (!Check(rc))
|
||||||
ThrowDBX(rc, "SQLRowCount", hstmt);
|
ThrowDBX(rc, "SQLPrepare", hstmt);
|
||||||
|
|
||||||
return afrw;
|
if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
|
||||||
} // endif ncol
|
ThrowDBX(rc, "SQLNumResultCols", hstmt);
|
||||||
|
|
||||||
|
if (ncol == 0) {
|
||||||
|
strcpy(g->Message, "This Srcdef does not return a result set");
|
||||||
|
return -1;
|
||||||
|
} // endif ncol
|
||||||
|
|
||||||
|
// Ok, now we can proceed
|
||||||
|
do {
|
||||||
|
rc = SQLExecute(hstmt);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(rc, "SQLExecute", hstmt);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(rc, "SQLExecDirect", hstmt);
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = SQLNumResultCols(hstmt, &ncol);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
|
} // endif Srcdef
|
||||||
|
|
||||||
for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
|
for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
|
||||||
if (!colp->IsSpecial())
|
if (!colp->IsSpecial())
|
||||||
@ -1407,10 +1476,33 @@ int ODBConn::PrepareSQL(char *sql)
|
|||||||
{
|
{
|
||||||
PGLOBAL& g = m_G;
|
PGLOBAL& g = m_G;
|
||||||
bool b;
|
bool b;
|
||||||
|
UINT txn = 0;
|
||||||
SWORD nparm;
|
SWORD nparm;
|
||||||
RETCODE rc;
|
RETCODE rc;
|
||||||
HSTMT hstmt;
|
HSTMT hstmt;
|
||||||
|
|
||||||
|
if (m_Tdb->GetMode() != MODE_READ) {
|
||||||
|
// Does the data source support transactions
|
||||||
|
rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
|
||||||
|
|
||||||
|
if (Check(rc) && txn != SQL_TC_NONE) try {
|
||||||
|
rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
|
||||||
|
SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
|
||||||
|
|
||||||
|
m_Transact = true;
|
||||||
|
} catch(DBX *x) {
|
||||||
|
if (trace)
|
||||||
|
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
||||||
|
htrc(x->m_ErrMsg[i]);
|
||||||
|
|
||||||
|
strcpy(g->Message, x->GetErrorMessage(0));
|
||||||
|
} // end try/catch
|
||||||
|
|
||||||
|
} // endif Mode
|
||||||
|
|
||||||
try {
|
try {
|
||||||
b = false;
|
b = false;
|
||||||
|
|
||||||
@ -1450,13 +1542,19 @@ int ODBConn::PrepareSQL(char *sql)
|
|||||||
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
|
||||||
htrc(x->m_ErrMsg[i]);
|
htrc(x->m_ErrMsg[i]);
|
||||||
|
|
||||||
strcpy(m_G->Message, x->GetErrorMessage(0));
|
strcpy(g->Message, x->GetErrorMessage(0));
|
||||||
|
|
||||||
if (b)
|
if (b)
|
||||||
SQLCancel(hstmt);
|
SQLCancel(hstmt);
|
||||||
|
|
||||||
rc = SQLFreeStmt(hstmt, SQL_DROP);
|
rc = SQLFreeStmt(hstmt, SQL_DROP);
|
||||||
m_hstmt = NULL;
|
m_hstmt = NULL;
|
||||||
|
|
||||||
|
if (m_Transact) {
|
||||||
|
rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
|
||||||
|
m_Transact = false;
|
||||||
|
} // endif m_Transact
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
} // end try/catch
|
} // end try/catch
|
||||||
|
|
||||||
@ -1465,27 +1563,59 @@ int ODBConn::PrepareSQL(char *sql)
|
|||||||
} // end of PrepareSQL
|
} // end of PrepareSQL
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* Bind a parameter for inserting. */
|
/* Execute a prepared statement. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
bool ODBConn::ExecuteSQL(void)
|
int ODBConn::ExecuteSQL(bool x)
|
||||||
{
|
{
|
||||||
RETCODE rc;
|
PGLOBAL& g = m_G;
|
||||||
|
SWORD ncol = 0;
|
||||||
|
RETCODE rc;
|
||||||
|
SQLLEN afrw = -1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rc = SQLExecute(m_hstmt);
|
do {
|
||||||
|
rc = SQLExecute(m_hstmt);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
if (!Check(rc))
|
if (!Check(rc))
|
||||||
ThrowDBX(rc, "SQLExecute", m_hstmt);
|
ThrowDBX(rc, "SQLExecute", m_hstmt);
|
||||||
|
|
||||||
|
if (!Check(SQLNumResultCols(m_hstmt, &ncol)))
|
||||||
|
ThrowDBX(rc, "SQLNumResultCols", m_hstmt);
|
||||||
|
|
||||||
|
if (ncol) {
|
||||||
|
if (x) {
|
||||||
|
afrw = ncol;
|
||||||
|
strcpy(g->Message, "Result set column number");
|
||||||
|
} else {
|
||||||
|
// This should never happen while inserting
|
||||||
|
strcpy(g->Message, "Logical error while inserting");
|
||||||
|
} // endif ncol
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Insert, Update or Delete statement
|
||||||
|
if (!Check(SQLRowCount(m_hstmt, &afrw)))
|
||||||
|
ThrowDBX(rc, "SQLRowCount", m_hstmt);
|
||||||
|
|
||||||
|
if (x)
|
||||||
|
strcpy(g->Message, "Affected rows");
|
||||||
|
|
||||||
|
} // endif ncol
|
||||||
|
|
||||||
} catch(DBX *x) {
|
} catch(DBX *x) {
|
||||||
strcpy(m_G->Message, x->GetErrorMessage(0));
|
strcpy(m_G->Message, x->GetErrorMessage(0));
|
||||||
SQLCancel(m_hstmt);
|
SQLCancel(m_hstmt);
|
||||||
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
|
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
|
||||||
m_hstmt = NULL;
|
m_hstmt = NULL;
|
||||||
return true;
|
|
||||||
|
if (m_Transact) {
|
||||||
|
rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
|
||||||
|
m_Transact = false;
|
||||||
|
} // endif m_Transact
|
||||||
|
|
||||||
} // end try/catch
|
} // end try/catch
|
||||||
|
|
||||||
return false;
|
return (int)afrw;
|
||||||
} // end of ExecuteSQL
|
} // end of ExecuteSQL
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -1537,6 +1667,132 @@ bool ODBConn::BindParam(ODBCCOL *colp)
|
|||||||
return false;
|
return false;
|
||||||
} // end of BindParam
|
} // end of BindParam
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/* GetMetaData: constructs the result blocks containing the */
|
||||||
|
/* description of all the columns of an SQL command. */
|
||||||
|
/**************************************************************************/
|
||||||
|
PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src)
|
||||||
|
{
|
||||||
|
static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_INT,
|
||||||
|
TYPE_SHORT, TYPE_SHORT};
|
||||||
|
static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC,
|
||||||
|
FLD_SCALE, FLD_NULL};
|
||||||
|
static unsigned int length[] = {0, 6, 10, 6, 6};
|
||||||
|
unsigned char cn[60];
|
||||||
|
int qcol = 5;
|
||||||
|
short nl, type, prec, nul, cns = (short)sizeof(cn);
|
||||||
|
PQRYRES qrp = NULL;
|
||||||
|
PCOLRES crp;
|
||||||
|
USHORT i;
|
||||||
|
SQLULEN n;
|
||||||
|
SWORD ncol;
|
||||||
|
RETCODE rc;
|
||||||
|
HSTMT hstmt;
|
||||||
|
|
||||||
|
if (Open(dsn, 10) < 1) // openReadOnly + noOdbcDialog
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
rc = SQLAllocStmt(m_hdbc, &hstmt);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
|
||||||
|
|
||||||
|
OnSetOptions(hstmt);
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = SQLPrepare(hstmt, (PUCHAR)src, SQL_NTS);
|
||||||
|
// rc = SQLExecDirect(hstmt, (PUCHAR)src, SQL_NTS);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(rc, "SQLExecDirect", hstmt);
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = SQLNumResultCols(hstmt, &ncol);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(rc, "SQLNumResultCols", hstmt);
|
||||||
|
|
||||||
|
if (ncol) for (i = 1; i <= ncol; i++) {
|
||||||
|
do {
|
||||||
|
rc = SQLDescribeCol(hstmt, i, NULL, 0, &nl, NULL, NULL, NULL, NULL);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(rc, "SQLDescribeCol", hstmt);
|
||||||
|
|
||||||
|
length[0] = max(length[0], (UINT)nl);
|
||||||
|
} // endfor i
|
||||||
|
|
||||||
|
} catch(DBX *x) {
|
||||||
|
strcpy(g->Message, x->GetErrorMessage(0));
|
||||||
|
goto err;
|
||||||
|
} // end try/catch
|
||||||
|
|
||||||
|
if (!ncol) {
|
||||||
|
strcpy(g->Message, "Invalid Srcdef");
|
||||||
|
goto err;
|
||||||
|
} // endif ncol
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* Allocate the structures used to refer to the result set. */
|
||||||
|
/************************************************************************/
|
||||||
|
qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3,
|
||||||
|
buftyp, fldtyp, length, false, true);
|
||||||
|
|
||||||
|
// Some columns must be renamed
|
||||||
|
for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
|
||||||
|
switch (++i) {
|
||||||
|
case 3: crp->Name = "Precision"; break;
|
||||||
|
case 4: crp->Name = "Scale"; break;
|
||||||
|
case 5: crp->Name = "Nullable"; break;
|
||||||
|
} // endswitch i
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* Now get the results into blocks. */
|
||||||
|
/************************************************************************/
|
||||||
|
try {
|
||||||
|
for (i = 0; i < ncol; i++) {
|
||||||
|
do {
|
||||||
|
rc = SQLDescribeCol(hstmt, i+1, cn, cns, &nl, &type, &n, &prec, &nul);
|
||||||
|
} while (rc == SQL_STILL_EXECUTING);
|
||||||
|
|
||||||
|
if (!Check(rc))
|
||||||
|
ThrowDBX(rc, "SQLDescribeCol", hstmt);
|
||||||
|
else
|
||||||
|
qrp->Nblin++;
|
||||||
|
|
||||||
|
crp = qrp->Colresp; // Column_Name
|
||||||
|
crp->Kdata->SetValue((char*)cn, i);
|
||||||
|
crp = crp->Next; // Data_Type
|
||||||
|
crp->Kdata->SetValue(type, i);
|
||||||
|
crp = crp->Next; // Precision (length)
|
||||||
|
crp->Kdata->SetValue((int)n, i);
|
||||||
|
crp = crp->Next; // Scale
|
||||||
|
crp->Kdata->SetValue(prec, i);
|
||||||
|
crp = crp->Next; // Nullable
|
||||||
|
crp->Kdata->SetValue(nul, i);
|
||||||
|
} // endfor i
|
||||||
|
|
||||||
|
} catch(DBX *x) {
|
||||||
|
strcpy(g->Message, x->GetErrorMessage(0));
|
||||||
|
qrp = NULL;
|
||||||
|
} // end try/catch
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
err:
|
||||||
|
SQLCancel(hstmt);
|
||||||
|
rc = SQLFreeStmt(hstmt, SQL_DROP);
|
||||||
|
Close();
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* Return the result pointer for use by GetData routines. */
|
||||||
|
/************************************************************************/
|
||||||
|
return qrp;
|
||||||
|
} // end of GetMetaData
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* Get the list of Data Sources and set it in qrp. */
|
/* Get the list of Data Sources and set it in qrp. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -1840,6 +2096,11 @@ void ODBConn::Close()
|
|||||||
} // endif m_hstmt
|
} // endif m_hstmt
|
||||||
|
|
||||||
if (m_hdbc != SQL_NULL_HDBC) {
|
if (m_hdbc != SQL_NULL_HDBC) {
|
||||||
|
if (m_Transact) {
|
||||||
|
rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_COMMIT);
|
||||||
|
m_Transact = false;
|
||||||
|
} // endif m_Transact
|
||||||
|
|
||||||
rc = SQLDisconnect(m_hdbc);
|
rc = SQLDisconnect(m_hdbc);
|
||||||
|
|
||||||
if (trace && rc != SQL_SUCCESS)
|
if (trace && rc != SQL_SUCCESS)
|
||||||
|
@ -91,8 +91,7 @@ class DBX : public BLOCK {
|
|||||||
// Implementation (use ThrowDBX to create)
|
// Implementation (use ThrowDBX to create)
|
||||||
RETCODE GetRC(void) {return m_RC;}
|
RETCODE GetRC(void) {return m_RC;}
|
||||||
PSZ GetMsg(void) {return m_Msg;}
|
PSZ GetMsg(void) {return m_Msg;}
|
||||||
const char *GetErrorMessage(int i)
|
const char *GetErrorMessage(int i);
|
||||||
{return (i >=0 && i < MAX_NUM_OF_MSG) ? m_ErrMsg[i] : "No ODBC error";}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void BuildErrorMessage(ODBConn* pdb, HSTMT hstmt = SQL_NULL_HSTMT);
|
void BuildErrorMessage(ODBConn* pdb, HSTMT hstmt = SQL_NULL_HSTMT);
|
||||||
@ -107,6 +106,7 @@ class DBX : public BLOCK {
|
|||||||
/* ODBConn class. */
|
/* ODBConn class. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
class ODBConn : public BLOCK {
|
class ODBConn : public BLOCK {
|
||||||
|
friend class TDBODBC;
|
||||||
friend class DBX;
|
friend class DBX;
|
||||||
friend PQRYRES GetColumnInfo(PGLOBAL, char*&, char *, int, PVBLK&);
|
friend PQRYRES GetColumnInfo(PGLOBAL, char*&, char *, int, PVBLK&);
|
||||||
private:
|
private:
|
||||||
@ -142,11 +142,12 @@ class ODBConn : public BLOCK {
|
|||||||
int ExecDirectSQL(char *sql, ODBCCOL *tocols);
|
int ExecDirectSQL(char *sql, ODBCCOL *tocols);
|
||||||
int Fetch(void);
|
int Fetch(void);
|
||||||
int PrepareSQL(char *sql);
|
int PrepareSQL(char *sql);
|
||||||
bool ExecuteSQL(void);
|
int ExecuteSQL(bool x);
|
||||||
bool BindParam(ODBCCOL *colp);
|
bool BindParam(ODBCCOL *colp);
|
||||||
int GetCatInfo(CATPARM *cap);
|
int GetCatInfo(CATPARM *cap);
|
||||||
bool GetDataSources(PQRYRES qrp);
|
bool GetDataSources(PQRYRES qrp);
|
||||||
bool GetDrivers(PQRYRES qrp);
|
bool GetDrivers(PQRYRES qrp);
|
||||||
|
PQRYRES GetMetaData(PGLOBAL g, char *dsn, char *src);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Set special options
|
// Set special options
|
||||||
@ -185,5 +186,6 @@ class ODBConn : public BLOCK {
|
|||||||
int m_Catver;
|
int m_Catver;
|
||||||
PSZ m_Connect;
|
PSZ m_Connect;
|
||||||
bool m_Updatable;
|
bool m_Updatable;
|
||||||
|
bool m_Transact;
|
||||||
char m_IDQuoteChar;
|
char m_IDQuoteChar;
|
||||||
}; // end of ODBConn class definition
|
}; // end of ODBConn class definition
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef _OS_H_INCLUDED
|
#ifndef _OS_H_INCLUDED
|
||||||
#define _OS_H_INCLUDED
|
#define _OS_H_INCLUDED
|
||||||
|
|
||||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
|
||||||
typedef off_t off64_t;
|
typedef off_t off64_t;
|
||||||
#define lseek64(fd, offset, whence) lseek((fd), (offset), (whence))
|
#define lseek64(fd, offset, whence) lseek((fd), (offset), (whence))
|
||||||
#define open64(path, flags, mode) open((path), (flags), (mode))
|
#define open64(path, flags, mode) open((path), (flags), (mode))
|
||||||
|
@ -111,6 +111,7 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
|
|||||||
TYPE_AM_PIVOT = 120, /* PIVOT access method type no */
|
TYPE_AM_PIVOT = 120, /* PIVOT access method type no */
|
||||||
TYPE_AM_SRC = 121, /* PIVOT multiple column type no */
|
TYPE_AM_SRC = 121, /* PIVOT multiple column type no */
|
||||||
TYPE_AM_FNC = 122, /* PIVOT source column type no */
|
TYPE_AM_FNC = 122, /* PIVOT source column type no */
|
||||||
|
TYPE_AM_XML = 127, /* XML access method type no */
|
||||||
TYPE_AM_XTB = 130, /* SYS table access method type */
|
TYPE_AM_XTB = 130, /* SYS table access method type */
|
||||||
TYPE_AM_MAC = 137, /* MAC table access method type */
|
TYPE_AM_MAC = 137, /* MAC table access method type */
|
||||||
TYPE_AM_WMI = 139, /* WMI table access method type */
|
TYPE_AM_WMI = 139, /* WMI table access method type */
|
||||||
@ -582,5 +583,6 @@ FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode);
|
|||||||
int global_open(GLOBAL *g, int msgid, const char *filename, int flags);
|
int global_open(GLOBAL *g, int msgid, const char *filename, int flags);
|
||||||
int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode);
|
int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode);
|
||||||
DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir);
|
DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir);
|
||||||
|
char *MakeEscape(PGLOBAL g, char* str, char q);
|
||||||
|
|
||||||
bool PushWarning(PGLOBAL, PTDBASE);
|
bool PushWarning(PGLOBAL, PTDBASE);
|
||||||
|
@ -731,6 +731,34 @@ bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
|
|||||||
return (b);
|
return (b);
|
||||||
} /* end of EvalLikePattern */
|
} /* end of EvalLikePattern */
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MakeEscape: Escape some characters in a string. */
|
||||||
|
/***********************************************************************/
|
||||||
|
char *MakeEscape(PGLOBAL g, char* str, char q)
|
||||||
|
{
|
||||||
|
char *bufp;
|
||||||
|
int i, k, n = 0, len = (int)strlen(str);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
if (str[i] == q || str[i] == '\\')
|
||||||
|
n++;
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return str;
|
||||||
|
else
|
||||||
|
bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
|
||||||
|
|
||||||
|
for (i = k = 0; i < len; i++) {
|
||||||
|
if (str[i] == q || str[i] == '\\')
|
||||||
|
bufp[k++] = '\\';
|
||||||
|
|
||||||
|
bufp[k++] = str[i];
|
||||||
|
} // endfor i
|
||||||
|
|
||||||
|
bufp[k] = 0;
|
||||||
|
return bufp;
|
||||||
|
} /* end of MakeEscape */
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* PlugConvertConstant: convert a Plug constant to an Xobject. */
|
/* PlugConvertConstant: convert a Plug constant to an Xobject. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
@ -147,6 +147,7 @@ class XMLNODELIST : public BLOCK {
|
|||||||
// Properties
|
// Properties
|
||||||
virtual int GetLength(void) = 0;
|
virtual int GetLength(void) = 0;
|
||||||
virtual PXNODE GetItem(PGLOBAL, int, PXNODE = NULL) = 0;
|
virtual PXNODE GetItem(PGLOBAL, int, PXNODE = NULL) = 0;
|
||||||
|
virtual bool DropItem(PGLOBAL, int) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Constructor
|
// Constructor
|
||||||
|
@ -93,7 +93,7 @@ PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q,
|
|||||||
PCOLRES crp;
|
PCOLRES crp;
|
||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
imax = 0;
|
imax = hmax = 0;
|
||||||
length[0] = 128;
|
length[0] = 128;
|
||||||
goto skipit;
|
goto skipit;
|
||||||
} // endif info
|
} // endif info
|
||||||
|
@ -884,9 +884,11 @@ void TDBDIR::CloseDB(PGLOBAL g)
|
|||||||
_findclose(Hsearch);
|
_findclose(Hsearch);
|
||||||
Hsearch = -1;
|
Hsearch = -1;
|
||||||
#else // !WIN32
|
#else // !WIN32
|
||||||
// Close the DIR handle.
|
// Close the DIR handle
|
||||||
closedir(Dir);
|
if (Dir) {
|
||||||
Dir = NULL;
|
closedir(Dir);
|
||||||
|
Dir = NULL;
|
||||||
|
} // endif dir
|
||||||
#endif // !WIN32
|
#endif // !WIN32
|
||||||
iFile = 0;
|
iFile = 0;
|
||||||
} // end of CloseDB
|
} // end of CloseDB
|
||||||
|
@ -86,6 +86,7 @@ MYSQLDEF::MYSQLDEF(void)
|
|||||||
Isview = FALSE;
|
Isview = FALSE;
|
||||||
Bind = FALSE;
|
Bind = FALSE;
|
||||||
Delayed = FALSE;
|
Delayed = FALSE;
|
||||||
|
Xsrc = FALSE;
|
||||||
} // end of MYSQLDEF constructor
|
} // end of MYSQLDEF constructor
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -171,7 +172,7 @@ bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
|
|||||||
/* true error */
|
/* true error */
|
||||||
/* */
|
/* */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
bool MYSQLDEF::ParseURL(PGLOBAL g, char *url)
|
bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
|
||||||
{
|
{
|
||||||
if ((!strstr(url, "://") && (!strchr(url, '@')))) {
|
if ((!strstr(url, "://") && (!strchr(url, '@')))) {
|
||||||
// No :// or @ in connection string. Must be a straight
|
// No :// or @ in connection string. Must be a straight
|
||||||
@ -248,34 +249,41 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url)
|
|||||||
if ((Database = strchr(Hostname, '/'))) {
|
if ((Database = strchr(Hostname, '/'))) {
|
||||||
*Database++ = 0;
|
*Database++ = 0;
|
||||||
|
|
||||||
if ((Tabname = strchr(Database, '/')))
|
if ((Tabname = strchr(Database, '/'))) {
|
||||||
*Tabname++ = 0;
|
*Tabname++ = 0;
|
||||||
|
|
||||||
// Make sure there's not an extra /
|
// Make sure there's not an extra /
|
||||||
if ((strchr(Tabname, '/'))) {
|
if ((strchr(Tabname, '/'))) {
|
||||||
strcpy(g->Message, "Syntax error in URL");
|
strcpy(g->Message, "Syntax error in URL");
|
||||||
return true;
|
return true;
|
||||||
} // endif /
|
} // endif /
|
||||||
|
|
||||||
|
} // endif Tabname
|
||||||
|
|
||||||
} // endif database
|
} // endif database
|
||||||
|
|
||||||
if ((sport = strchr(Hostname, ':')))
|
if ((sport = strchr(Hostname, ':')))
|
||||||
*sport++ = 0;
|
*sport++ = 0;
|
||||||
|
|
||||||
Portnumber = (sport && sport[0]) ? atoi(sport) : GetDefaultPort();
|
// For unspecified values, get the values of old style options
|
||||||
|
// but only if called from MYSQLDEF, else set them to NULL
|
||||||
|
Portnumber = (sport && sport[0]) ? atoi(sport)
|
||||||
|
: (b) ? Cat->GetIntCatInfo("Port", GetDefaultPort()) : 0;
|
||||||
|
|
||||||
if (Username[0] == 0)
|
if (Username[0] == 0)
|
||||||
Username = Cat->GetStringCatInfo(g, "User", "*");
|
Username = (b) ? Cat->GetStringCatInfo(g, "User", "*") : NULL;
|
||||||
|
|
||||||
if (Hostname[0] == 0)
|
if (Hostname[0] == 0)
|
||||||
Hostname = Cat->GetStringCatInfo(g, "Host", "localhost");
|
Hostname = (b) ? Cat->GetStringCatInfo(g, "Host", "localhost") : NULL;
|
||||||
|
|
||||||
if (!Database || !*Database)
|
if (!Database || !*Database)
|
||||||
Database = Cat->GetStringCatInfo(g, "Database", "*");
|
Database = (b) ? Cat->GetStringCatInfo(g, "Database", "*") : NULL;
|
||||||
|
|
||||||
if (!Tabname || !*Tabname)
|
if (!Tabname || !*Tabname)
|
||||||
Tabname = Name;
|
Tabname = (b) ? Cat->GetStringCatInfo(g, "Tabname", Name) : NULL;
|
||||||
|
|
||||||
|
if (!Password)
|
||||||
|
Password = (b) ? Cat->GetStringCatInfo(g, "Password", NULL) : NULL;
|
||||||
} // endif URL
|
} // endif URL
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@ -347,6 +355,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
|||||||
if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL)))
|
if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL)))
|
||||||
Isview = TRUE;
|
Isview = TRUE;
|
||||||
|
|
||||||
|
Xsrc = Cat->GetBoolCatInfo("Execsrc", FALSE);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
} // end of DefineAM
|
} // end of DefineAM
|
||||||
|
|
||||||
@ -355,7 +364,9 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
|
PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
|
||||||
{
|
{
|
||||||
if (Catfunc == FNC_COL)
|
if (Xsrc)
|
||||||
|
return new(g) TDBMYEXC(this);
|
||||||
|
else if (Catfunc == FNC_COL)
|
||||||
return new(g) TDBMCL(this);
|
return new(g) TDBMCL(this);
|
||||||
else
|
else
|
||||||
return new(g) TDBMYSQL(this);
|
return new(g) TDBMYSQL(this);
|
||||||
@ -507,6 +518,9 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g)
|
|||||||
if (To_Filter)
|
if (To_Filter)
|
||||||
strcat(strcat(Query, " WHERE "), To_Filter);
|
strcat(strcat(Query, " WHERE "), To_Filter);
|
||||||
|
|
||||||
|
if (trace)
|
||||||
|
htrc("Query=%s\n", Query);
|
||||||
|
|
||||||
// Now we know how much to suballocate
|
// Now we know how much to suballocate
|
||||||
PlugSubAlloc(g, NULL, strlen(Query) + 1);
|
PlugSubAlloc(g, NULL, strlen(Query) + 1);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -1040,18 +1054,20 @@ int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
void TDBMYSQL::CloseDB(PGLOBAL g)
|
void TDBMYSQL::CloseDB(PGLOBAL g)
|
||||||
{
|
{
|
||||||
if (Mode == MODE_INSERT) {
|
if (Myc.Connected()) {
|
||||||
char cmd[64];
|
if (Mode == MODE_INSERT) {
|
||||||
int w;
|
char cmd[64];
|
||||||
PDBUSER dup = PlgGetUser(g);
|
int w;
|
||||||
|
PDBUSER dup = PlgGetUser(g);
|
||||||
|
|
||||||
dup->Step = "Enabling indexes";
|
dup->Step = "Enabling indexes";
|
||||||
sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
|
sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
|
||||||
Myc.m_Rows = -1; // To execute the query
|
Myc.m_Rows = -1; // To execute the query
|
||||||
m_Rc = Myc.ExecSQL(g, cmd, &w);
|
m_Rc = Myc.ExecSQL(g, cmd, &w);
|
||||||
} // endif m_Rc
|
} // endif m_Rc
|
||||||
|
|
||||||
Myc.Close();
|
Myc.Close();
|
||||||
|
} // endif Myc
|
||||||
|
|
||||||
if (trace)
|
if (trace)
|
||||||
htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
|
htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
|
||||||
@ -1275,6 +1291,212 @@ void MYSQLCOL::WriteColumn(PGLOBAL g)
|
|||||||
|
|
||||||
} // end of WriteColumn
|
} // end of WriteColumn
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Implementation of the TDBMYSQL class. */
|
||||||
|
/***********************************************************************/
|
||||||
|
|
||||||
|
// Is this really useful ???
|
||||||
|
PTDB TDBMYEXC::CopyOne(PTABS t)
|
||||||
|
{
|
||||||
|
PTDB tp;
|
||||||
|
PCOL cp1, cp2;
|
||||||
|
PGLOBAL g = t->G;
|
||||||
|
|
||||||
|
tp = new(g) TDBMYEXC(g, this);
|
||||||
|
|
||||||
|
for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
|
||||||
|
cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);
|
||||||
|
|
||||||
|
NewPointer(t, cp1, cp2);
|
||||||
|
} // endfor cp1
|
||||||
|
|
||||||
|
return tp;
|
||||||
|
} // end of CopyOne
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Allocate MYSQL column description block. */
|
||||||
|
/***********************************************************************/
|
||||||
|
PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
||||||
|
{
|
||||||
|
PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);
|
||||||
|
|
||||||
|
if (!colp->Flag)
|
||||||
|
Cmdcol = colp->GetName();
|
||||||
|
|
||||||
|
return colp;
|
||||||
|
} // end of MakeCol
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MakeCMD: make the SQL statement to send to MYSQL connection. */
|
||||||
|
/***********************************************************************/
|
||||||
|
char *TDBMYEXC::MakeCMD(PGLOBAL g)
|
||||||
|
{
|
||||||
|
char *xcmd = NULL;
|
||||||
|
|
||||||
|
if (To_Filter) {
|
||||||
|
if (Cmdcol) {
|
||||||
|
char col[128], cmd[1024];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
memset(cmd, 0, sizeof(cmd));
|
||||||
|
n = sscanf(To_Filter, "%s = '%1023c", col, cmd);
|
||||||
|
|
||||||
|
if (n == 2 && !stricmp(col, Cmdcol)) {
|
||||||
|
xcmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
|
||||||
|
|
||||||
|
strcpy(xcmd, cmd);
|
||||||
|
xcmd[strlen(xcmd) - 1] = 0;
|
||||||
|
} else
|
||||||
|
strcpy(g->Message, "Invalid command specification filter");
|
||||||
|
|
||||||
|
} else
|
||||||
|
strcpy(g->Message, "No command column in select list");
|
||||||
|
|
||||||
|
} else if (!Srcdef)
|
||||||
|
strcpy(g->Message, "No Srcdef default command");
|
||||||
|
else
|
||||||
|
xcmd = Srcdef;
|
||||||
|
|
||||||
|
return xcmd;
|
||||||
|
} // end of MakeCMD
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* EXC GetMaxSize: returns the maximum number of rows in the table. */
|
||||||
|
/***********************************************************************/
|
||||||
|
int TDBMYEXC::GetMaxSize(PGLOBAL g)
|
||||||
|
{
|
||||||
|
if (MaxSize < 0) {
|
||||||
|
MaxSize = 1;
|
||||||
|
} // endif MaxSize
|
||||||
|
|
||||||
|
return MaxSize;
|
||||||
|
} // end of GetMaxSize
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MySQL Exec Access Method opening routine. */
|
||||||
|
/***********************************************************************/
|
||||||
|
bool TDBMYEXC::OpenDB(PGLOBAL g)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (Use == USE_OPEN) {
|
||||||
|
strcpy(g->Message, "Multiple execution is not allowed");
|
||||||
|
return true;
|
||||||
|
} // endif use
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
/* Open a MySQL connection for this table. */
|
||||||
|
/* Note: this may not be the proper way to do. Perhaps it is better */
|
||||||
|
/* to test whether a connection is already open for this server */
|
||||||
|
/* and if so to allocate just a new result set. But this only for */
|
||||||
|
/* servers allowing concurency in getting results ??? */
|
||||||
|
/*********************************************************************/
|
||||||
|
if (!Myc.Connected())
|
||||||
|
if (Myc.Open(g, Host, Database, User, Pwd, Port))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Use = USE_OPEN; // Do it now in case we are recursively called
|
||||||
|
|
||||||
|
if (Mode != MODE_READ) {
|
||||||
|
strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
|
||||||
|
return true;
|
||||||
|
} // endif Mode
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
/* Get the command to execute. */
|
||||||
|
/*********************************************************************/
|
||||||
|
if (!(Query = MakeCMD(g))) {
|
||||||
|
Myc.Close();
|
||||||
|
return true;
|
||||||
|
} // endif Query
|
||||||
|
|
||||||
|
if ((rc = Myc.ExecSQL(g, Query)) == RC_NF) {
|
||||||
|
strcpy(g->Message, "Affected rows");
|
||||||
|
AftRows = Myc.m_Rows;
|
||||||
|
} else if (rc == RC_OK) {
|
||||||
|
sprintf(g->Message, "Columns and %d rows", Myc.m_Rows);
|
||||||
|
AftRows = Myc.m_Fields;
|
||||||
|
} else
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} // end of OpenDB
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Data Base read routine for MYSQL access method. */
|
||||||
|
/***********************************************************************/
|
||||||
|
int TDBMYEXC::ReadDB(PGLOBAL g)
|
||||||
|
{
|
||||||
|
return (++N) ? RC_EF : RC_OK;
|
||||||
|
} // end of ReadDB
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* WriteDB: Data Base write routine for Exec MYSQL access methods. */
|
||||||
|
/***********************************************************************/
|
||||||
|
int TDBMYEXC::WriteDB(PGLOBAL g)
|
||||||
|
{
|
||||||
|
strcpy(g->Message, "EXEC MYSQL tables are read only");
|
||||||
|
return RC_FX;
|
||||||
|
} // end of WriteDB
|
||||||
|
|
||||||
|
// ------------------------- MYXCOL functions ---------------------------
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MYXCOL public constructor. */
|
||||||
|
/***********************************************************************/
|
||||||
|
MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
|
||||||
|
: MYSQLCOL(cdp, tdbp, cprec, i, am)
|
||||||
|
{
|
||||||
|
// Set additional EXEC MYSQL access method information for column.
|
||||||
|
Flag = cdp->GetOffset();
|
||||||
|
} // end of MYSQLCOL constructor
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MYSQLCOL public constructor. */
|
||||||
|
/***********************************************************************/
|
||||||
|
MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
|
||||||
|
: MYSQLCOL(fld, tdbp, i, am)
|
||||||
|
{
|
||||||
|
if (trace)
|
||||||
|
htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
|
||||||
|
|
||||||
|
} // end of MYSQLCOL constructor
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MYXCOL constructor used for copying columns. */
|
||||||
|
/* tdbp is the pointer to the new table descriptor. */
|
||||||
|
/***********************************************************************/
|
||||||
|
MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
|
||||||
|
{
|
||||||
|
Flag = col1->Flag;
|
||||||
|
} // end of MYXCOL copy constructor
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* ReadColumn: */
|
||||||
|
/***********************************************************************/
|
||||||
|
void MYXCOL::ReadColumn(PGLOBAL g)
|
||||||
|
{
|
||||||
|
PTDBMYX tdbp = (PTDBMYX)To_Tdb;
|
||||||
|
|
||||||
|
switch (Flag) {
|
||||||
|
case 0: Value->SetValue_psz(tdbp->Query); break;
|
||||||
|
case 1: Value->SetValue(tdbp->AftRows); break;
|
||||||
|
case 2: Value->SetValue_psz(g->Message); break;
|
||||||
|
default: Value->SetValue_psz("Invalid Flag"); break;
|
||||||
|
} // endswitch Flag
|
||||||
|
|
||||||
|
} // end of ReadColumn
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* WriteColumn: should never be called. */
|
||||||
|
/***********************************************************************/
|
||||||
|
void MYXCOL::WriteColumn(PGLOBAL g)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
} // end of WriteColumn
|
||||||
|
|
||||||
/* ---------------------------TDBMCL class --------------------------- */
|
/* ---------------------------TDBMCL class --------------------------- */
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
typedef class MYSQLDEF *PMYDEF;
|
typedef class MYSQLDEF *PMYDEF;
|
||||||
typedef class TDBMYSQL *PTDBMY;
|
typedef class TDBMYSQL *PTDBMY;
|
||||||
typedef class MYSQLC *PMYC;
|
|
||||||
typedef class MYSQLCOL *PMYCOL;
|
typedef class MYSQLCOL *PMYCOL;
|
||||||
|
typedef class TDBMYEXC *PTDBMYX;
|
||||||
|
typedef class MYXCOL *PMYXCOL;
|
||||||
|
typedef class MYSQLC *PMYC;
|
||||||
|
|
||||||
/* ------------------------- MYSQL classes --------------------------- */
|
/* ------------------------- MYSQL classes --------------------------- */
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ class MYSQLDEF : public TABDEF {/* Logical table description */
|
|||||||
// Methods
|
// Methods
|
||||||
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
|
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
|
||||||
virtual PTDB GetTable(PGLOBAL g, MODE m);
|
virtual PTDB GetTable(PGLOBAL g, MODE m);
|
||||||
bool ParseURL(PGLOBAL g, char *url);
|
bool ParseURL(PGLOBAL g, char *url, bool b = true);
|
||||||
bool GetServerInfo(PGLOBAL g, const char *server_name);
|
bool GetServerInfo(PGLOBAL g, const char *server_name);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -54,6 +56,7 @@ class MYSQLDEF : public TABDEF {/* Logical table description */
|
|||||||
bool Isview; /* TRUE if this table is a MySQL view */
|
bool Isview; /* TRUE if this table is a MySQL view */
|
||||||
bool Bind; /* Use prepared statement on insert */
|
bool Bind; /* Use prepared statement on insert */
|
||||||
bool Delayed; /* Delayed insert */
|
bool Delayed; /* Delayed insert */
|
||||||
|
bool Xsrc; /* Execution type */
|
||||||
}; // end of MYSQLDEF
|
}; // end of MYSQLDEF
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -158,6 +161,87 @@ class MYSQLCOL : public COLBLK {
|
|||||||
int Rank; // Rank (position) number in the query
|
int Rank; // Rank (position) number in the query
|
||||||
}; // end of class MYSQLCOL
|
}; // end of class MYSQLCOL
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* This is the class declaration for the exec command MYSQL table. */
|
||||||
|
/***********************************************************************/
|
||||||
|
class TDBMYEXC : public TDBMYSQL {
|
||||||
|
friend class MYXCOL;
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp) {Cmdcol = NULL;}
|
||||||
|
TDBMYEXC(PGLOBAL g, PTDBMYX tdbp) : TDBMYSQL(g, tdbp)
|
||||||
|
{Cmdcol = tdbp->Cmdcol;}
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
//virtual AMT GetAmType(void) {return TYPE_AM_MYSQL;}
|
||||||
|
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYEXC(g, this);}
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
virtual PTDB CopyOne(PTABS t);
|
||||||
|
//virtual int GetAffectedRows(void) {return AftRows;}
|
||||||
|
//virtual int GetRecpos(void) {return N;}
|
||||||
|
//virtual int GetProgMax(PGLOBAL g);
|
||||||
|
//virtual void ResetDB(void) {N = 0;}
|
||||||
|
//virtual int RowNumber(PGLOBAL g, bool b = FALSE);
|
||||||
|
virtual bool IsView(void) {return Isview;}
|
||||||
|
//virtual PSZ GetServer(void) {return Server;}
|
||||||
|
// void SetDatabase(LPCSTR db) {Database = (char*)db;}
|
||||||
|
|
||||||
|
// Database routines
|
||||||
|
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
|
||||||
|
virtual int GetMaxSize(PGLOBAL g);
|
||||||
|
virtual bool OpenDB(PGLOBAL g);
|
||||||
|
virtual int ReadDB(PGLOBAL g);
|
||||||
|
virtual int WriteDB(PGLOBAL g);
|
||||||
|
//virtual int DeleteDB(PGLOBAL g, int irc);
|
||||||
|
//virtual void CloseDB(PGLOBAL g);
|
||||||
|
|
||||||
|
// Specific routines
|
||||||
|
// bool SetColumnRanks(PGLOBAL g);
|
||||||
|
// PCOL MakeFieldColumn(PGLOBAL g, char *name);
|
||||||
|
// PSZ FindFieldColumn(char *name);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Internal functions
|
||||||
|
char *MakeCMD(PGLOBAL g);
|
||||||
|
//bool MakeSelect(PGLOBAL g);
|
||||||
|
//bool MakeInsert(PGLOBAL g);
|
||||||
|
//int BindColumns(PGLOBAL g);
|
||||||
|
|
||||||
|
// Members
|
||||||
|
char *Cmdcol; // The name of the Xsrc command column
|
||||||
|
}; // end of class TDBMYEXC
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Class MYXCOL: MySQL exec command table column. */
|
||||||
|
/***********************************************************************/
|
||||||
|
class MYXCOL : public MYSQLCOL {
|
||||||
|
friend class TDBMYEXC;
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "MYSQL");
|
||||||
|
MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am = "MYSQL");
|
||||||
|
MYXCOL(MYXCOL *colp, PTDB tdbp); // Constructor used in copy process
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
//virtual int GetAmType(void) {return TYPE_AM_MYSQL;}
|
||||||
|
// void InitBind(PGLOBAL g);
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
|
||||||
|
virtual void ReadColumn(PGLOBAL g);
|
||||||
|
virtual void WriteColumn(PGLOBAL g);
|
||||||
|
// bool FindRank(PGLOBAL g);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Default constructor not to be used
|
||||||
|
MYXCOL(void) {}
|
||||||
|
|
||||||
|
// Members
|
||||||
|
char *Buffer; // To get returned message
|
||||||
|
int Flag; // Column content desc
|
||||||
|
}; // end of class MYXCOL
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* This is the class declaration for the MYSQL column catalog table. */
|
/* This is the class declaration for the MYSQL column catalog table. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
|
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
|
||||||
/* PROGRAM NAME: TABODBC */
|
/* PROGRAM NAME: TABODBC */
|
||||||
/* ------------- */
|
/* ------------- */
|
||||||
/* Version 2.5 */
|
/* Version 2.6 */
|
||||||
/* */
|
/* */
|
||||||
/* COPYRIGHT: */
|
/* COPYRIGHT: */
|
||||||
/* ---------- */
|
/* ---------- */
|
||||||
@ -90,8 +90,9 @@ extern int num_read, num_there, num_eq[2]; // Statistics
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
ODBCDEF::ODBCDEF(void)
|
ODBCDEF::ODBCDEF(void)
|
||||||
{
|
{
|
||||||
Connect = Tabname = Tabowner = Tabqual = Qchar = NULL;
|
Connect = Tabname = Tabowner = Tabqual = Srcdef = Qchar = NULL;
|
||||||
Catver = Options = 0;
|
Catver = Options = 0;
|
||||||
|
Xsrc = false;
|
||||||
} // end of ODBCDEF constructor
|
} // end of ODBCDEF constructor
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -99,17 +100,17 @@ ODBCDEF::ODBCDEF(void)
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
||||||
{
|
{
|
||||||
int dop = ODBConn::noOdbcDialog; // Default for options
|
|
||||||
|
|
||||||
Desc = Connect = Cat->GetStringCatInfo(g, "Connect", "");
|
Desc = Connect = Cat->GetStringCatInfo(g, "Connect", "");
|
||||||
Tabname = Cat->GetStringCatInfo(g, "Name",
|
Tabname = Cat->GetStringCatInfo(g, "Name",
|
||||||
(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
|
(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
|
||||||
Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname);
|
Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname);
|
||||||
Tabowner = Cat->GetStringCatInfo(g, "Owner", "");
|
Tabowner = Cat->GetStringCatInfo(g, "Owner", "");
|
||||||
Tabqual = Cat->GetStringCatInfo(g, "Qualifier", "");
|
Tabqual = Cat->GetStringCatInfo(g, "Qualifier", "");
|
||||||
|
Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL);
|
||||||
Qchar = Cat->GetStringCatInfo(g, "Qchar", "");
|
Qchar = Cat->GetStringCatInfo(g, "Qchar", "");
|
||||||
Catver = Cat->GetIntCatInfo("Catver", 2);
|
Catver = Cat->GetIntCatInfo("Catver", 2);
|
||||||
Options = Cat->GetIntCatInfo("Options", dop);
|
Xsrc = Cat->GetBoolCatInfo("Execsrc", FALSE);
|
||||||
|
Options = ODBConn::noOdbcDialog;
|
||||||
Pseudo = 2; // FILID is Ok but not ROWID
|
Pseudo = 2; // FILID is Ok but not ROWID
|
||||||
return false;
|
return false;
|
||||||
} // end of DefineAM
|
} // end of DefineAM
|
||||||
@ -125,7 +126,9 @@ PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m)
|
|||||||
/* Allocate a TDB of the proper type. */
|
/* Allocate a TDB of the proper type. */
|
||||||
/* Column blocks will be allocated only when needed. */
|
/* Column blocks will be allocated only when needed. */
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
switch (Catfunc) {
|
if (Xsrc)
|
||||||
|
tdbp = new(g) TDBXDBC(this);
|
||||||
|
else switch (Catfunc) {
|
||||||
case FNC_COL:
|
case FNC_COL:
|
||||||
tdbp = new(g) TDBOCL(this);
|
tdbp = new(g) TDBOCL(this);
|
||||||
break;
|
break;
|
||||||
@ -161,19 +164,21 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
|
|||||||
Cnp = NULL;
|
Cnp = NULL;
|
||||||
|
|
||||||
if (tdp) {
|
if (tdp) {
|
||||||
Connect = tdp->GetConnect();
|
Connect = tdp->Connect;
|
||||||
TableName = tdp->GetTabname();
|
TableName = tdp->Tabname;
|
||||||
Owner = tdp->GetTabowner();
|
Owner = tdp->Tabowner;
|
||||||
Qualifier = tdp->GetTabqual();
|
Qualifier = tdp->Tabqual;
|
||||||
|
Srcdef = tdp->Srcdef;
|
||||||
Quote = tdp->GetQchar();
|
Quote = tdp->GetQchar();
|
||||||
Options = tdp->GetOptions();
|
Options = tdp->Options;
|
||||||
Rows = tdp->GetElemt();
|
Rows = tdp->GetElemt();
|
||||||
Catver = tdp->GetCatver();
|
Catver = tdp->Catver;
|
||||||
} else {
|
} else {
|
||||||
Connect = NULL;
|
Connect = NULL;
|
||||||
TableName = NULL;
|
TableName = NULL;
|
||||||
Owner = NULL;
|
Owner = NULL;
|
||||||
Qualifier = NULL;
|
Qualifier = NULL;
|
||||||
|
Srcdef = NULL;
|
||||||
Quote = NULL;
|
Quote = NULL;
|
||||||
Options = 0;
|
Options = 0;
|
||||||
Rows = 0;
|
Rows = 0;
|
||||||
@ -201,6 +206,7 @@ TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
|
|||||||
TableName = tdbp->TableName;
|
TableName = tdbp->TableName;
|
||||||
Owner = tdbp->Owner;
|
Owner = tdbp->Owner;
|
||||||
Qualifier = tdbp->Qualifier;
|
Qualifier = tdbp->Qualifier;
|
||||||
|
Srcdef = tdbp->Srcdef;
|
||||||
Quote = tdbp->Quote;
|
Quote = tdbp->Quote;
|
||||||
Query = tdbp->Query;
|
Query = tdbp->Query;
|
||||||
Count = tdbp->Count;
|
Count = tdbp->Count;
|
||||||
@ -299,7 +305,6 @@ void TDBODBC::SetFile(PGLOBAL g, PSZ fn)
|
|||||||
DBQ = fn;
|
DBQ = fn;
|
||||||
} // end of SetFile
|
} // end of SetFile
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
/* Convert an UTF-8 string to latin characters. */
|
/* Convert an UTF-8 string to latin characters. */
|
||||||
/******************************************************************/
|
/******************************************************************/
|
||||||
@ -314,7 +319,6 @@ int TDBODBC::Decode(char *txt, char *buf, size_t n)
|
|||||||
return 0;
|
return 0;
|
||||||
} // end of Decode
|
} // end of Decode
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* MakeSQL: make the SQL statement use with ODBC connection. */
|
/* MakeSQL: make the SQL statement use with ODBC connection. */
|
||||||
/* Note: when implementing EOM filtering, column only used in local */
|
/* Note: when implementing EOM filtering, column only used in local */
|
||||||
@ -329,6 +333,9 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
|
|||||||
PTABLE tablep = To_Table;
|
PTABLE tablep = To_Table;
|
||||||
PCOL colp;
|
PCOL colp;
|
||||||
|
|
||||||
|
if (Srcdef)
|
||||||
|
return Srcdef;
|
||||||
|
|
||||||
if (!cnt) {
|
if (!cnt) {
|
||||||
// Normal SQL statement to retrieve results
|
// Normal SQL statement to retrieve results
|
||||||
for (colp = Columns; colp; colp = colp->GetNext())
|
for (colp = Columns; colp; colp = colp->GetNext())
|
||||||
@ -430,6 +437,83 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
|
|||||||
return sql;
|
return sql;
|
||||||
} // end of MakeSQL
|
} // end of MakeSQL
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MakeInsert: make the Insert statement used with ODBC connection. */
|
||||||
|
/***********************************************************************/
|
||||||
|
bool TDBODBC::MakeInsert(PGLOBAL g)
|
||||||
|
{
|
||||||
|
char *colist, *valist;
|
||||||
|
// char *tk = "`";
|
||||||
|
int len = 0;
|
||||||
|
bool b = FALSE;
|
||||||
|
PCOL colp;
|
||||||
|
|
||||||
|
if (Query)
|
||||||
|
return false; // already done
|
||||||
|
|
||||||
|
for (colp = Columns; colp; colp = colp->GetNext())
|
||||||
|
if (colp->IsSpecial()) {
|
||||||
|
strcpy(g->Message, MSG(NO_ODBC_SPECOL));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
len += (strlen(colp->GetName()) + 4);
|
||||||
|
((PODBCCOL)colp)->Rank = ++Nparm;
|
||||||
|
} // endif colp
|
||||||
|
|
||||||
|
colist = (char*)PlugSubAlloc(g, NULL, len);
|
||||||
|
*colist = '\0';
|
||||||
|
valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
|
||||||
|
*valist = '\0';
|
||||||
|
|
||||||
|
for (colp = Columns; colp; colp = colp->GetNext()) {
|
||||||
|
if (b) {
|
||||||
|
strcat(colist, ", ");
|
||||||
|
strcat(valist, ",");
|
||||||
|
} else
|
||||||
|
b = true;
|
||||||
|
|
||||||
|
if (Quote)
|
||||||
|
strcat(strcat(strcat(colist, Quote), colp->GetName()), Quote);
|
||||||
|
else
|
||||||
|
strcat(colist, colp->GetName());
|
||||||
|
|
||||||
|
strcat(valist, "?"); // Parameter marker
|
||||||
|
} // endfor colp
|
||||||
|
|
||||||
|
// Below 32 is enough to contain the fixed part of the query
|
||||||
|
len = (strlen(TableName) + strlen(colist) + strlen(valist) + 32);
|
||||||
|
Query = (char*)PlugSubAlloc(g, NULL, len);
|
||||||
|
strcpy(Query, "INSERT INTO ");
|
||||||
|
|
||||||
|
if (Quote)
|
||||||
|
strcat(strcat(strcat(Query, Quote), TableName), Quote);
|
||||||
|
else
|
||||||
|
strcat(Query, TableName);
|
||||||
|
|
||||||
|
strcat(strcat(strcat(Query, " ("), colist), ") VALUES (");
|
||||||
|
strcat(strcat(Query, valist), ")");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} // end of MakeInsert
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* ODBC Bind Parameter function. */
|
||||||
|
/***********************************************************************/
|
||||||
|
bool TDBODBC::BindParameters(PGLOBAL g)
|
||||||
|
{
|
||||||
|
PODBCCOL colp;
|
||||||
|
|
||||||
|
for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) {
|
||||||
|
colp->AllocateBuffers(g, 0);
|
||||||
|
|
||||||
|
if (Ocp->BindParam(colp))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} // endfor colp
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} // end of BindParameters
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* ResetSize: call by TDBMUL when calculating size estimate. */
|
/* ResetSize: call by TDBMUL when calculating size estimate. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -448,6 +532,16 @@ void TDBODBC::ResetSize(void)
|
|||||||
int TDBODBC::GetMaxSize(PGLOBAL g)
|
int TDBODBC::GetMaxSize(PGLOBAL g)
|
||||||
{
|
{
|
||||||
if (MaxSize < 0) {
|
if (MaxSize < 0) {
|
||||||
|
// Make MariaDB happy
|
||||||
|
MaxSize = 100;
|
||||||
|
#if 0
|
||||||
|
// This is unuseful and takes time
|
||||||
|
if (Srcdef) {
|
||||||
|
// Return a reasonable guess
|
||||||
|
MaxSize = 100;
|
||||||
|
return MaxSize;
|
||||||
|
} // endif Srcdef
|
||||||
|
|
||||||
if (!Ocp)
|
if (!Ocp)
|
||||||
Ocp = new(g) ODBConn(g, this);
|
Ocp = new(g) ODBConn(g, this);
|
||||||
|
|
||||||
@ -466,19 +560,18 @@ int TDBODBC::GetMaxSize(PGLOBAL g)
|
|||||||
|
|
||||||
if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0)
|
if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0)
|
||||||
return -3;
|
return -3;
|
||||||
|
#endif // 0
|
||||||
} // endif MaxSize
|
} // endif MaxSize
|
||||||
|
|
||||||
return MaxSize;
|
return MaxSize;
|
||||||
} // end of GetMaxSize
|
} // end of GetMaxSize
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* Return 0 in mode DELETE or UPDATE to tell that it is done. */
|
/* Return max size value. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
int TDBODBC::GetProgMax(PGLOBAL g)
|
int TDBODBC::GetProgMax(PGLOBAL g)
|
||||||
{
|
{
|
||||||
return (Mode == MODE_DELETE || Mode == MODE_UPDATE) ? 0
|
return GetMaxSize(g);
|
||||||
: GetMaxSize(g);
|
|
||||||
} // end of GetProgMax
|
} // end of GetProgMax
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -546,14 +639,24 @@ bool TDBODBC::OpenDB(PGLOBAL g)
|
|||||||
if (!colp->IsSpecial())
|
if (!colp->IsSpecial())
|
||||||
colp->AllocateBuffers(g, Rows);
|
colp->AllocateBuffers(g, Rows);
|
||||||
|
|
||||||
} else
|
} else {
|
||||||
rc = true;
|
Ocp->Close();
|
||||||
|
return true;
|
||||||
|
} // endif Query
|
||||||
|
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0);
|
rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0);
|
||||||
|
|
||||||
|
} else if (Mode == MODE_INSERT) {
|
||||||
|
if (!(rc = MakeInsert(g)))
|
||||||
|
if (Nparm != Ocp->PrepareSQL(Query)) {
|
||||||
|
strcpy(g->Message, MSG(PARM_CNT_MISS));
|
||||||
|
rc = true;
|
||||||
|
} else
|
||||||
|
rc = BindParameters(g);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
strcpy(g->Message, "ODBC tables are read only in this version");
|
strcpy(g->Message, "No DELETE/UPDATE of ODBC tablesd");
|
||||||
return true;
|
return true;
|
||||||
} // endelse
|
} // endelse
|
||||||
|
|
||||||
@ -592,30 +695,6 @@ int TDBODBC::ReadDB(PGLOBAL g)
|
|||||||
// Direct access of ODBC tables is not implemented yet
|
// Direct access of ODBC tables is not implemented yet
|
||||||
strcpy(g->Message, MSG(NO_ODBC_DIRECT));
|
strcpy(g->Message, MSG(NO_ODBC_DIRECT));
|
||||||
longjmp(g->jumper[g->jump_level], GetAmType());
|
longjmp(g->jumper[g->jump_level], GetAmType());
|
||||||
|
|
||||||
#if 0
|
|
||||||
/*******************************************************************/
|
|
||||||
/* Reading is by an index table. */
|
|
||||||
/*******************************************************************/
|
|
||||||
int recpos = To_Kindex->Fetch(g);
|
|
||||||
|
|
||||||
switch (recpos) {
|
|
||||||
case -1: // End of file reached
|
|
||||||
return RC_EF;
|
|
||||||
case -2: // No match for join
|
|
||||||
return RC_NF;
|
|
||||||
case -3: // Same record as current one
|
|
||||||
num_there++;
|
|
||||||
return RC_OK;
|
|
||||||
default:
|
|
||||||
/***************************************************************/
|
|
||||||
/* Set the cursor position according to record to read. */
|
|
||||||
/***************************************************************/
|
|
||||||
//--------------------------------- TODO --------------------------------
|
|
||||||
break;
|
|
||||||
} // endswitch recpos
|
|
||||||
#endif // 0
|
|
||||||
|
|
||||||
} // endif To_Kindex
|
} // endif To_Kindex
|
||||||
|
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
@ -641,8 +720,15 @@ int TDBODBC::ReadDB(PGLOBAL g)
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
int TDBODBC::WriteDB(PGLOBAL g)
|
int TDBODBC::WriteDB(PGLOBAL g)
|
||||||
{
|
{
|
||||||
strcpy(g->Message, "ODBC tables are read only");
|
int n = Ocp->ExecuteSQL(false);
|
||||||
return RC_FX;
|
|
||||||
|
if (n < 0) {
|
||||||
|
AftRows = n;
|
||||||
|
return RC_FX;
|
||||||
|
} else
|
||||||
|
AftRows += n;
|
||||||
|
|
||||||
|
return RC_OK;
|
||||||
} // end of WriteDB
|
} // end of WriteDB
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -664,7 +750,8 @@ void TDBODBC::CloseDB(PGLOBAL g)
|
|||||||
// To_Kindex = NULL;
|
// To_Kindex = NULL;
|
||||||
// } // endif
|
// } // endif
|
||||||
|
|
||||||
Ocp->Close();
|
if (Ocp)
|
||||||
|
Ocp->Close();
|
||||||
|
|
||||||
if (trace)
|
if (trace)
|
||||||
htrc("ODBC CloseDB: closing %s\n", Name);
|
htrc("ODBC CloseDB: closing %s\n", Name);
|
||||||
@ -892,7 +979,7 @@ void ODBCCOL::WriteColumn(PGLOBAL g)
|
|||||||
/* Do convert the column value if necessary. */
|
/* Do convert the column value if necessary. */
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
if (Value != To_Val)
|
if (Value != To_Val)
|
||||||
Value->SetValue_pval(To_Val, false); // Convert the inserted value
|
Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
|
||||||
|
|
||||||
if (Buf_Type == TYPE_DATE) {
|
if (Buf_Type == TYPE_DATE) {
|
||||||
struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm);
|
struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm);
|
||||||
@ -903,8 +990,245 @@ void ODBCCOL::WriteColumn(PGLOBAL g)
|
|||||||
Sqlbuf->day = dbtime->tm_mday;
|
Sqlbuf->day = dbtime->tm_mday;
|
||||||
Sqlbuf->month = dbtime->tm_mon + 1;
|
Sqlbuf->month = dbtime->tm_mon + 1;
|
||||||
Sqlbuf->year = dbtime->tm_year + 1900;
|
Sqlbuf->year = dbtime->tm_year + 1900;
|
||||||
|
Sqlbuf->fraction = 0;
|
||||||
} // endif Buf_Type
|
} // endif Buf_Type
|
||||||
|
|
||||||
|
if (Nullable)
|
||||||
|
*StrLen = (Value->IsNull()) ? SQL_NULL_DATA :
|
||||||
|
(IsTypeNum(Buf_Type)) ? 0 : SQL_NTS;
|
||||||
|
|
||||||
|
} // end of WriteColumn
|
||||||
|
|
||||||
|
/* -------------------------- Class TDBXDBC -------------------------- */
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Implementation of the TDBODBC class. */
|
||||||
|
/***********************************************************************/
|
||||||
|
PTDB TDBXDBC::CopyOne(PTABS t)
|
||||||
|
{
|
||||||
|
PTDB tp;
|
||||||
|
PXSRCCOL cp1, cp2;
|
||||||
|
PGLOBAL g = t->G; // Is this really useful ???
|
||||||
|
|
||||||
|
tp = new(g) TDBXDBC(this);
|
||||||
|
|
||||||
|
for (cp1 = (PXSRCCOL)Columns; cp1; cp1 = (PXSRCCOL)cp1->GetNext()) {
|
||||||
|
cp2 = new(g) XSRCCOL(cp1, tp); // Make a copy
|
||||||
|
NewPointer(t, cp1, cp2);
|
||||||
|
} // endfor cp1
|
||||||
|
|
||||||
|
return tp;
|
||||||
|
} // end of CopyOne
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Allocate XSRC column description block. */
|
||||||
|
/***********************************************************************/
|
||||||
|
PCOL TDBXDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
||||||
|
{
|
||||||
|
PXSRCCOL colp = new(g) XSRCCOL(cdp, this, cprec, n);
|
||||||
|
|
||||||
|
if (!colp->Flag)
|
||||||
|
Cmdcol = colp->GetName();
|
||||||
|
|
||||||
|
return colp;
|
||||||
|
} // end of MakeCol
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* MakeCMD: make the SQL statement to send to ODBC connection. */
|
||||||
|
/***********************************************************************/
|
||||||
|
char *TDBXDBC::MakeCMD(PGLOBAL g)
|
||||||
|
{
|
||||||
|
char *xcmd = NULL;
|
||||||
|
|
||||||
|
if (To_Filter) {
|
||||||
|
if (Cmdcol) {
|
||||||
|
char col[128], cmd[1024];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
memset(cmd, 0, sizeof(cmd));
|
||||||
|
n = sscanf(To_Filter, "%s = '%1023c", col, cmd);
|
||||||
|
|
||||||
|
if (n == 2 && !stricmp(col, Cmdcol)) {
|
||||||
|
xcmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
|
||||||
|
|
||||||
|
strcpy(xcmd, cmd);
|
||||||
|
xcmd[strlen(xcmd) - 1] = 0;
|
||||||
|
} else
|
||||||
|
strcpy(g->Message, "Invalid command specification filter");
|
||||||
|
|
||||||
|
} else
|
||||||
|
strcpy(g->Message, "No command column in select list");
|
||||||
|
|
||||||
|
} else if (!Srcdef)
|
||||||
|
strcpy(g->Message, "No Srcdef default command");
|
||||||
|
else
|
||||||
|
xcmd = Srcdef;
|
||||||
|
|
||||||
|
return xcmd;
|
||||||
|
} // end of MakeCMD
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/***********************************************************************/
|
||||||
|
/* ODBC Bind Parameter function. */
|
||||||
|
/***********************************************************************/
|
||||||
|
bool TDBXDBC::BindParameters(PGLOBAL g)
|
||||||
|
{
|
||||||
|
PODBCCOL colp;
|
||||||
|
|
||||||
|
for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) {
|
||||||
|
colp->AllocateBuffers(g, 0);
|
||||||
|
|
||||||
|
if (Ocp->BindParam(colp))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} // endfor colp
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} // end of BindParameters
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* XDBC GetMaxSize: returns table size (always one row). */
|
||||||
|
/***********************************************************************/
|
||||||
|
int TDBXDBC::GetMaxSize(PGLOBAL g)
|
||||||
|
{
|
||||||
|
if (MaxSize < 0)
|
||||||
|
MaxSize = 1;
|
||||||
|
|
||||||
|
return MaxSize;
|
||||||
|
} // end of GetMaxSize
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* ODBC Access Method opening routine. */
|
||||||
|
/* New method now that this routine is called recursively (last table */
|
||||||
|
/* first in reverse order): index blocks are immediately linked to */
|
||||||
|
/* join block of next table if it exists or else are discarted. */
|
||||||
|
/***********************************************************************/
|
||||||
|
bool TDBXDBC::OpenDB(PGLOBAL g)
|
||||||
|
{
|
||||||
|
bool rc = false;
|
||||||
|
|
||||||
|
if (g->Trace)
|
||||||
|
htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n",
|
||||||
|
this, Tdb_No, Use, Mode);
|
||||||
|
|
||||||
|
if (Use == USE_OPEN) {
|
||||||
|
strcpy(g->Message, "Multiple execution is not allowed");
|
||||||
|
return true;
|
||||||
|
} // endif use
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
/* Open an ODBC connection for this table. */
|
||||||
|
/* Note: this may not be the proper way to do. Perhaps it is better */
|
||||||
|
/* to test whether a connection is already open for this datasource */
|
||||||
|
/* and if so to allocate just a new result set. But this only for */
|
||||||
|
/* drivers allowing concurency in getting results ??? */
|
||||||
|
/*********************************************************************/
|
||||||
|
if (!Ocp)
|
||||||
|
Ocp = new(g) ODBConn(g, this);
|
||||||
|
else if (Ocp->IsOpen())
|
||||||
|
Ocp->Close();
|
||||||
|
|
||||||
|
if (Ocp->Open(Connect, Options) < 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Use = USE_OPEN; // Do it now in case we are recursively called
|
||||||
|
|
||||||
|
if (Mode != MODE_READ) {
|
||||||
|
strcpy(g->Message, "No INSERT/DELETE/UPDATE of XDBC tables");
|
||||||
|
return true;
|
||||||
|
} // endif Mode
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
/* Get the command to execute. */
|
||||||
|
/*********************************************************************/
|
||||||
|
if (!(Query = MakeCMD(g))) {
|
||||||
|
Ocp->Close();
|
||||||
|
return true;
|
||||||
|
} // endif Query
|
||||||
|
|
||||||
|
Rows = 1;
|
||||||
|
|
||||||
|
if (Ocp->PrepareSQL(Query)) {
|
||||||
|
strcpy(g->Message, "Parameters not supported");
|
||||||
|
AftRows = -1;
|
||||||
|
} else
|
||||||
|
AftRows = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} // end of OpenDB
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* ReadDB: Data Base read routine for xdbc access method. */
|
||||||
|
/***********************************************************************/
|
||||||
|
int TDBXDBC::ReadDB(PGLOBAL g)
|
||||||
|
{
|
||||||
|
if (trace)
|
||||||
|
htrc("XDBC ReadDB: query=%s\n", SVP(Query));
|
||||||
|
|
||||||
|
if (Rows--) {
|
||||||
|
if (!AftRows)
|
||||||
|
AftRows = Ocp->ExecuteSQL(true);
|
||||||
|
|
||||||
|
} else
|
||||||
|
return RC_EF;
|
||||||
|
|
||||||
|
Fpos++; // Used for progress info
|
||||||
|
return RC_OK;
|
||||||
|
} // end of ReadDB
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Data Base delete line routine for ODBC access method. */
|
||||||
|
/***********************************************************************/
|
||||||
|
int TDBXDBC::WriteDB(PGLOBAL g)
|
||||||
|
{
|
||||||
|
strcpy(g->Message, "Execsrc tables are read only");
|
||||||
|
return RC_FX;
|
||||||
|
} // end of DeleteDB
|
||||||
|
|
||||||
|
/* --------------------------- XSRCCOL ------------------------------- */
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* XSRCCOL public constructor. */
|
||||||
|
/***********************************************************************/
|
||||||
|
XSRCCOL::XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
|
||||||
|
: ODBCCOL(cdp, tdbp, cprec, i, am)
|
||||||
|
{
|
||||||
|
// Set additional ODBC access method information for column.
|
||||||
|
Flag = cdp->GetOffset();
|
||||||
|
} // end of XSRCCOL constructor
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* XSRCCOL constructor used for copying columns. */
|
||||||
|
/* tdbp is the pointer to the new table descriptor. */
|
||||||
|
/***********************************************************************/
|
||||||
|
XSRCCOL::XSRCCOL(XSRCCOL *col1, PTDB tdbp) : ODBCCOL(col1, tdbp)
|
||||||
|
{
|
||||||
|
Flag = col1->Flag;
|
||||||
|
} // end of XSRCCOL copy constructor
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* ReadColumn: set column value according to Flag. */
|
||||||
|
/***********************************************************************/
|
||||||
|
void XSRCCOL::ReadColumn(PGLOBAL g)
|
||||||
|
{
|
||||||
|
PTDBXDBC tdbp = (PTDBXDBC)To_Tdb;
|
||||||
|
|
||||||
|
switch (Flag) {
|
||||||
|
case 0: Value->SetValue_psz(tdbp->Query); break;
|
||||||
|
case 1: Value->SetValue(tdbp->AftRows); break;
|
||||||
|
case 2: Value->SetValue_psz(g->Message); break;
|
||||||
|
default: Value->SetValue_psz("Invalid Flag"); break;
|
||||||
|
} // endswitch Flag
|
||||||
|
|
||||||
|
} // end of ReadColumn
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* WriteColumn: Should never be called. */
|
||||||
|
/***********************************************************************/
|
||||||
|
void XSRCCOL::WriteColumn(PGLOBAL g)
|
||||||
|
{
|
||||||
|
// Should never be called
|
||||||
} // end of WriteColumn
|
} // end of WriteColumn
|
||||||
|
|
||||||
/* ---------------------------TDBSRC class --------------------------- */
|
/* ---------------------------TDBSRC class --------------------------- */
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*************** Tabodbc H Declares Source Code File (.H) **************/
|
/*************** Tabodbc H Declares Source Code File (.H) **************/
|
||||||
/* Name: TABODBC.H Version 1.5 */
|
/* Name: TABODBC.H Version 1.6 */
|
||||||
/* */
|
/* */
|
||||||
/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */
|
/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */
|
||||||
/* */
|
/* */
|
||||||
@ -11,6 +11,8 @@
|
|||||||
typedef class ODBCDEF *PODEF;
|
typedef class ODBCDEF *PODEF;
|
||||||
typedef class TDBODBC *PTDBODBC;
|
typedef class TDBODBC *PTDBODBC;
|
||||||
typedef class ODBCCOL *PODBCCOL;
|
typedef class ODBCCOL *PODBCCOL;
|
||||||
|
typedef class TDBXDBC *PTDBXDBC;
|
||||||
|
typedef class XSRCCOL *PXSRCCOL;
|
||||||
typedef class TDBOIF *PTDBOIF;
|
typedef class TDBOIF *PTDBOIF;
|
||||||
typedef class OIFCOL *POIFCOL;
|
typedef class OIFCOL *POIFCOL;
|
||||||
typedef class TDBSRC *PTDBSRC;
|
typedef class TDBSRC *PTDBSRC;
|
||||||
@ -19,6 +21,8 @@ typedef class TDBSRC *PTDBSRC;
|
|||||||
/* ODBC table. */
|
/* ODBC table. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
class DllExport ODBCDEF : public TABDEF { /* Logical table description */
|
class DllExport ODBCDEF : public TABDEF { /* Logical table description */
|
||||||
|
friend class TDBODBC;
|
||||||
|
friend class TDBXDBC;
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
ODBCDEF(void);
|
ODBCDEF(void);
|
||||||
@ -29,6 +33,7 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
|
|||||||
PSZ GetTabname(void) {return Tabname;}
|
PSZ GetTabname(void) {return Tabname;}
|
||||||
PSZ GetTabowner(void) {return Tabowner;}
|
PSZ GetTabowner(void) {return Tabowner;}
|
||||||
PSZ GetTabqual(void) {return Tabqual;}
|
PSZ GetTabqual(void) {return Tabqual;}
|
||||||
|
PSZ GetSrcdef(void) {return Srcdef;}
|
||||||
PSZ GetQchar(void) {return (Qchar && *Qchar) ? Qchar : NULL;}
|
PSZ GetQchar(void) {return (Qchar && *Qchar) ? Qchar : NULL;}
|
||||||
int GetCatver(void) {return Catver;}
|
int GetCatver(void) {return Catver;}
|
||||||
int GetOptions(void) {return Options;}
|
int GetOptions(void) {return Options;}
|
||||||
@ -43,9 +48,11 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
|
|||||||
PSZ Tabname; /* External table name */
|
PSZ Tabname; /* External table name */
|
||||||
PSZ Tabowner; /* External table owner */
|
PSZ Tabowner; /* External table owner */
|
||||||
PSZ Tabqual; /* External table qualifier */
|
PSZ Tabqual; /* External table qualifier */
|
||||||
|
PSZ Srcdef; /* The source table SQL definition */
|
||||||
PSZ Qchar; /* Identifier quoting character */
|
PSZ Qchar; /* Identifier quoting character */
|
||||||
int Catver; /* ODBC version for catalog functions */
|
int Catver; /* ODBC version for catalog functions */
|
||||||
int Options; /* Open connection options */
|
int Options; /* Open connection options */
|
||||||
|
bool Xsrc; /* Execution type */
|
||||||
}; // end of ODBCDEF
|
}; // end of ODBCDEF
|
||||||
|
|
||||||
#if !defined(NODBC)
|
#if !defined(NODBC)
|
||||||
@ -92,10 +99,10 @@ class TDBODBC : public TDBASE {
|
|||||||
int Decode(char *utf, char *buf, size_t n);
|
int Decode(char *utf, char *buf, size_t n);
|
||||||
char *MakeSQL(PGLOBAL g, bool cnt);
|
char *MakeSQL(PGLOBAL g, bool cnt);
|
||||||
//bool MakeUpdate(PGLOBAL g, PSELECT selist);
|
//bool MakeUpdate(PGLOBAL g, PSELECT selist);
|
||||||
//bool MakeInsert(PGLOBAL g);
|
bool MakeInsert(PGLOBAL g);
|
||||||
//bool MakeDelete(PGLOBAL g);
|
//bool MakeDelete(PGLOBAL g);
|
||||||
//bool MakeFilter(PGLOBAL g, bool c);
|
//bool MakeFilter(PGLOBAL g, bool c);
|
||||||
//bool BindParameters(PGLOBAL g);
|
bool BindParameters(PGLOBAL g);
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
ODBConn *Ocp; // Points to an ODBC connection class
|
ODBConn *Ocp; // Points to an ODBC connection class
|
||||||
@ -104,6 +111,7 @@ class TDBODBC : public TDBASE {
|
|||||||
char *TableName; // Points to ODBC table name
|
char *TableName; // Points to ODBC table name
|
||||||
char *Owner; // Points to ODBC table Owner
|
char *Owner; // Points to ODBC table Owner
|
||||||
char *Qualifier; // Points to ODBC table Qualifier
|
char *Qualifier; // Points to ODBC table Qualifier
|
||||||
|
char *Srcdef; // The source table SQL definition
|
||||||
char *Query; // Points to SQL statement
|
char *Query; // Points to SQL statement
|
||||||
char *Count; // Points to count(*) SQL statement
|
char *Count; // Points to count(*) SQL statement
|
||||||
//char *Where; // Points to local where clause
|
//char *Where; // Points to local where clause
|
||||||
@ -122,7 +130,7 @@ class TDBODBC : public TDBASE {
|
|||||||
}; // end of class TDBODBC
|
}; // end of class TDBODBC
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* Class ODBCCOL: DOS access method column descriptor. */
|
/* Class ODBCCOL: ODBC access method column descriptor. */
|
||||||
/* This A.M. is used for ODBC tables. */
|
/* This A.M. is used for ODBC tables. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
class ODBCCOL : public COLBLK {
|
class ODBCCOL : public COLBLK {
|
||||||
@ -153,16 +161,85 @@ class ODBCCOL : public COLBLK {
|
|||||||
ODBCCOL(void);
|
ODBCCOL(void);
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
|
TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
|
||||||
void *Bufp; // To extended buffer
|
void *Bufp; // To extended buffer
|
||||||
PVBLK Blkp; // To Value Block
|
PVBLK Blkp; // To Value Block
|
||||||
//char F_Date[12]; // Internal Date format
|
//char F_Date[12]; // Internal Date format
|
||||||
PVAL To_Val; // To value used for Insert
|
PVAL To_Val; // To value used for Insert
|
||||||
SQLLEN *StrLen; // As returned by ODBC
|
SQLLEN *StrLen; // As returned by ODBC
|
||||||
SQLLEN Slen; // Used with Fetch
|
SQLLEN Slen; // Used with Fetch
|
||||||
int Rank; // Rank (position) number in the query
|
int Rank; // Rank (position) number in the query
|
||||||
}; // end of class ODBCCOL
|
}; // end of class ODBCCOL
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* This is the ODBC Access Method class declaration that send */
|
||||||
|
/* commands to be executed by other DB ODBC drivers. */
|
||||||
|
/***********************************************************************/
|
||||||
|
class TDBXDBC : public TDBODBC {
|
||||||
|
friend class XSRCCOL;
|
||||||
|
friend class ODBConn;
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
TDBXDBC(PODEF tdp = NULL) : TDBODBC(tdp) {Cmdcol = NULL;}
|
||||||
|
TDBXDBC(PTDBXDBC tdbp) : TDBODBC(tdbp) {Cmdcol = tdbp->Cmdcol;}
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
//virtual AMT GetAmType(void) {return TYPE_AM_ODBC;}
|
||||||
|
virtual PTDB Duplicate(PGLOBAL g)
|
||||||
|
{return (PTDB)new(g) TDBXDBC(this);}
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
virtual PTDB CopyOne(PTABS t);
|
||||||
|
//virtual int GetRecpos(void);
|
||||||
|
//virtual PSZ GetFile(PGLOBAL g);
|
||||||
|
//virtual void SetFile(PGLOBAL g, PSZ fn);
|
||||||
|
//virtual void ResetSize(void);
|
||||||
|
//virtual int GetAffectedRows(void) {return AftRows;}
|
||||||
|
//virtual PSZ GetServer(void) {return "ODBC";}
|
||||||
|
|
||||||
|
// Database routines
|
||||||
|
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
|
||||||
|
//virtual int GetProgMax(PGLOBAL g);
|
||||||
|
virtual int GetMaxSize(PGLOBAL g);
|
||||||
|
virtual bool OpenDB(PGLOBAL g);
|
||||||
|
virtual int ReadDB(PGLOBAL g);
|
||||||
|
virtual int WriteDB(PGLOBAL g);
|
||||||
|
//virtual int DeleteDB(PGLOBAL g, int irc);
|
||||||
|
//virtual void CloseDB(PGLOBAL g);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Internal functions
|
||||||
|
char *MakeCMD(PGLOBAL g);
|
||||||
|
//bool BindParameters(PGLOBAL g);
|
||||||
|
|
||||||
|
// Members
|
||||||
|
char *Cmdcol; // The name of the Xsrc command column
|
||||||
|
}; // end of class TDBXDBC
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Used by table in source execute mode. */
|
||||||
|
/***********************************************************************/
|
||||||
|
class XSRCCOL : public ODBCCOL {
|
||||||
|
friend class TDBXDBC;
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "ODBC");
|
||||||
|
XSRCCOL(XSRCCOL *colp, PTDB tdbp); // Constructor used in copy process
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
//virtual int GetAmType(void) {return TYPE_AM_ODBC;}
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
virtual void ReadColumn(PGLOBAL g);
|
||||||
|
virtual void WriteColumn(PGLOBAL g);
|
||||||
|
// void Print(PGLOBAL g, FILE *, uint);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Members
|
||||||
|
char *Buffer; // To get returned message
|
||||||
|
int Flag; // Column content desc
|
||||||
|
}; // end of class XSRCCOL
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* This is the class declaration for the Data Sources catalog table. */
|
/* This is the class declaration for the Data Sources catalog table. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
|
@ -591,6 +591,17 @@ bool PRXCOL::Init(PGLOBAL g)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
} // end of Init
|
} // end of Init
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* Reset the column descriptor to non evaluated yet. */
|
||||||
|
/***********************************************************************/
|
||||||
|
void PRXCOL::Reset(void)
|
||||||
|
{
|
||||||
|
if (Colp)
|
||||||
|
Colp->Reset();
|
||||||
|
|
||||||
|
Status &= ~BUF_READ;
|
||||||
|
} // end of Reset
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* ReadColumn: */
|
/* ReadColumn: */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
@ -600,7 +611,7 @@ void PRXCOL::ReadColumn(PGLOBAL g)
|
|||||||
htrc("PRX ReadColumn: name=%s\n", Name);
|
htrc("PRX ReadColumn: name=%s\n", Name);
|
||||||
|
|
||||||
if (Colp) {
|
if (Colp) {
|
||||||
Colp->ReadColumn(g);
|
Colp->Eval(g);
|
||||||
Value->SetValue_pval(To_Val);
|
Value->SetValue_pval(To_Val);
|
||||||
|
|
||||||
// Set null when applicable
|
// Set null when applicable
|
||||||
|
@ -70,7 +70,7 @@ class DllExport TDBPRX : public TDBASE {
|
|||||||
virtual int GetRecpos(void) {return Tdbp->GetRecpos();}
|
virtual int GetRecpos(void) {return Tdbp->GetRecpos();}
|
||||||
virtual void ResetDB(void) {Tdbp->ResetDB();}
|
virtual void ResetDB(void) {Tdbp->ResetDB();}
|
||||||
virtual int RowNumber(PGLOBAL g, bool b = FALSE);
|
virtual int RowNumber(PGLOBAL g, bool b = FALSE);
|
||||||
virtual PSZ GetServer(void) {return (Tdbp) ? Tdbp->GetServer() : (char*) "?";}
|
virtual PSZ GetServer(void) {return (Tdbp) ? Tdbp->GetServer() : (PSZ)"?";}
|
||||||
|
|
||||||
// Database routines
|
// Database routines
|
||||||
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
|
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
|
||||||
@ -105,6 +105,7 @@ class DllExport PRXCOL : public COLBLK {
|
|||||||
virtual int GetAmType(void) {return TYPE_AM_PRX;}
|
virtual int GetAmType(void) {return TYPE_AM_PRX;}
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
virtual void Reset(void);
|
||||||
virtual bool IsSpecial(void) {return Pseudo;}
|
virtual bool IsSpecial(void) {return Pseudo;}
|
||||||
virtual void ReadColumn(PGLOBAL g);
|
virtual void ReadColumn(PGLOBAL g);
|
||||||
bool Init(PGLOBAL g);
|
bool Init(PGLOBAL g);
|
||||||
|
@ -253,7 +253,7 @@ XCLCOL::XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
|
|||||||
void XCLCOL::ReadColumn(PGLOBAL g)
|
void XCLCOL::ReadColumn(PGLOBAL g)
|
||||||
{
|
{
|
||||||
if (((PTDBXCL)To_Tdb)->New) {
|
if (((PTDBXCL)To_Tdb)->New) {
|
||||||
Colp->ReadColumn(g);
|
Colp->Eval(g);
|
||||||
strcpy(Cbuf, To_Val->GetCharValue());
|
strcpy(Cbuf, To_Val->GetCharValue());
|
||||||
Cp = Cbuf;
|
Cp = Cbuf;
|
||||||
} // endif New
|
} // endif New
|
||||||
@ -272,9 +272,11 @@ void XCLCOL::ReadColumn(PGLOBAL g)
|
|||||||
} else if (Nullable) {
|
} else if (Nullable) {
|
||||||
Value->Reset();
|
Value->Reset();
|
||||||
Value->SetNull(true);
|
Value->SetNull(true);
|
||||||
} else
|
} else {
|
||||||
// Skip that row
|
// Skip that row
|
||||||
((PTDBXCL)To_Tdb)->RowFlag = 2;
|
((PTDBXCL)To_Tdb)->RowFlag = 2;
|
||||||
|
Colp->Reset();
|
||||||
|
} // endif Cp
|
||||||
|
|
||||||
if (Cp && *Cp)
|
if (Cp && *Cp)
|
||||||
// More to come from the same row
|
// More to come from the same row
|
||||||
|
@ -90,7 +90,7 @@ class XCLCOL : public PRXCOL {
|
|||||||
XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
|
XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
virtual void Reset(void) {} // Evaluated only by TDBXCL
|
virtual void Reset(void) {Colp->Reset();} // Evaluated only by TDBXCL
|
||||||
virtual void ReadColumn(PGLOBAL g);
|
virtual void ReadColumn(PGLOBAL g);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -78,7 +78,9 @@ XMLDEF::XMLDEF(void)
|
|||||||
DefNs = NULL;
|
DefNs = NULL;
|
||||||
Attrib = NULL;
|
Attrib = NULL;
|
||||||
Hdattr = NULL;
|
Hdattr = NULL;
|
||||||
|
Coltype = 1;
|
||||||
Limit = 0;
|
Limit = 0;
|
||||||
|
Header = 0;
|
||||||
Xpand = false;
|
Xpand = false;
|
||||||
Usedom = false;
|
Usedom = false;
|
||||||
} // end of XMLDEF constructor
|
} // end of XMLDEF constructor
|
||||||
@ -338,17 +340,14 @@ PCOL TDBXML::InsertSpecialColumn(PGLOBAL g, PCOL colp)
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* LoadTableFile: Load and parse an XML file. */
|
/* LoadTableFile: Load and parse an XML file. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
int TDBXML::LoadTableFile(PGLOBAL g)
|
int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
|
||||||
{
|
{
|
||||||
char filename[_MAX_PATH];
|
|
||||||
int rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2;
|
int rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2;
|
||||||
PFBLOCK fp = NULL;
|
PFBLOCK fp = NULL;
|
||||||
PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
|
PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
|
||||||
|
|
||||||
/*********************************************************************/
|
if (Docp)
|
||||||
/* We used the file name relative to recorded datapath. */
|
return rc; // Already done
|
||||||
/*********************************************************************/
|
|
||||||
PlugSetPath(filename, Xfile, GetPath());
|
|
||||||
|
|
||||||
if (trace)
|
if (trace)
|
||||||
htrc("TDBXML: loading %s\n", filename);
|
htrc("TDBXML: loading %s\n", filename);
|
||||||
@ -397,6 +396,7 @@ int TDBXML::LoadTableFile(PGLOBAL g)
|
|||||||
} else
|
} else
|
||||||
rc = (errno == ENOENT) ? RC_NF : RC_INFO;
|
rc = (errno == ENOENT) ? RC_NF : RC_INFO;
|
||||||
|
|
||||||
|
// Cannot make a Xblock until document is made
|
||||||
return rc;
|
return rc;
|
||||||
} // endif Docp
|
} // endif Docp
|
||||||
|
|
||||||
@ -418,9 +418,8 @@ int TDBXML::LoadTableFile(PGLOBAL g)
|
|||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
bool TDBXML::Initialize(PGLOBAL g)
|
bool TDBXML::Initialize(PGLOBAL g)
|
||||||
{
|
{
|
||||||
char tabpath[64];
|
int rc;
|
||||||
int rc;
|
PXMLCOL colp;
|
||||||
PXMLCOL colp;
|
|
||||||
|
|
||||||
if (Void)
|
if (Void)
|
||||||
return false;
|
return false;
|
||||||
@ -440,8 +439,13 @@ bool TDBXML::Initialize(PGLOBAL g)
|
|||||||
#else
|
#else
|
||||||
if (!Root) {
|
if (!Root) {
|
||||||
#endif
|
#endif
|
||||||
|
char tabpath[64], filename[_MAX_PATH];
|
||||||
|
|
||||||
|
// We used the file name relative to recorded datapath
|
||||||
|
PlugSetPath(filename, Xfile, GetPath());
|
||||||
|
|
||||||
// Load or re-use the table file
|
// Load or re-use the table file
|
||||||
rc = LoadTableFile(g);
|
rc = LoadTableFile(g, filename);
|
||||||
|
|
||||||
if (rc == RC_OK) {
|
if (rc == RC_OK) {
|
||||||
// Get root node
|
// Get root node
|
||||||
@ -503,6 +507,9 @@ bool TDBXML::Initialize(PGLOBAL g)
|
|||||||
goto error;
|
goto error;
|
||||||
} // endif NewDoc
|
} // endif NewDoc
|
||||||
|
|
||||||
|
// Now we can link the Xblock
|
||||||
|
To_Xb = Docp->LinkXblock(g, Mode, rc, filename);
|
||||||
|
|
||||||
// Add a CONNECT comment node
|
// Add a CONNECT comment node
|
||||||
// sprintf(buf, MSG(CREATED_PLUGDB), version);
|
// sprintf(buf, MSG(CREATED_PLUGDB), version);
|
||||||
sprintf(buf, " Created by CONNECT %s ", version);
|
sprintf(buf, " Created by CONNECT %s ", version);
|
||||||
@ -893,12 +900,21 @@ int TDBXML::DeleteDB(PGLOBAL g, int irc)
|
|||||||
if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) {
|
if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) {
|
||||||
sprintf(g->Message, MSG(MISSING_ROWNODE), Irow);
|
sprintf(g->Message, MSG(MISSING_ROWNODE), Irow);
|
||||||
return RC_FX;
|
return RC_FX;
|
||||||
} else
|
} else {
|
||||||
TabNode->DeleteChild(g, RowNode);
|
TabNode->DeleteChild(g, RowNode);
|
||||||
|
|
||||||
|
if (Nlist->DropItem(g, Irow))
|
||||||
|
return RC_FX;
|
||||||
|
|
||||||
|
} // endif RowNode
|
||||||
|
|
||||||
Changed = true;
|
Changed = true;
|
||||||
} else if (irc != RC_EF) {
|
} else if (irc != RC_EF) {
|
||||||
TabNode->DeleteChild(g, RowNode);
|
TabNode->DeleteChild(g, RowNode);
|
||||||
|
|
||||||
|
if (Nlist->DropItem(g, Irow))
|
||||||
|
return RC_FX;
|
||||||
|
|
||||||
Changed = true;
|
Changed = true;
|
||||||
} // endif's irc
|
} // endif's irc
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
/* */
|
/* */
|
||||||
/* This file contains the XML table classes declares. */
|
/* This file contains the XML table classes declares. */
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
#define TYPE_AM_XML (AMT)127
|
|
||||||
|
|
||||||
typedef class XMLDEF *PXMLDEF;
|
typedef class XMLDEF *PXMLDEF;
|
||||||
typedef class TDBXML *PTDBXML;
|
typedef class TDBXML *PTDBXML;
|
||||||
typedef class XMLCOL *PXMLCOL;
|
typedef class XMLCOL *PXMLCOL;
|
||||||
@ -81,7 +79,7 @@ class DllExport TDBXML : public TDBASE {
|
|||||||
virtual void ResetDB(void) {N = 0;}
|
virtual void ResetDB(void) {N = 0;}
|
||||||
virtual void ResetSize(void) {MaxSize = -1;}
|
virtual void ResetSize(void) {MaxSize = -1;}
|
||||||
virtual int RowNumber(PGLOBAL g, bool b = false);
|
virtual int RowNumber(PGLOBAL g, bool b = false);
|
||||||
int LoadTableFile(PGLOBAL g);
|
int LoadTableFile(PGLOBAL g, char *filename);
|
||||||
bool Initialize(PGLOBAL g);
|
bool Initialize(PGLOBAL g);
|
||||||
bool SetTabNode(PGLOBAL g);
|
bool SetTabNode(PGLOBAL g);
|
||||||
void SetNodeAttr(PGLOBAL g, char *attr, PXNODE node);
|
void SetNodeAttr(PGLOBAL g, char *attr, PXNODE node);
|
||||||
|
@ -447,13 +447,12 @@ int TYPBLK<TYPE>::Find(PVAL vp)
|
|||||||
template <class TYPE>
|
template <class TYPE>
|
||||||
int TYPBLK<TYPE>::GetMaxLength(void)
|
int TYPBLK<TYPE>::GetMaxLength(void)
|
||||||
{
|
{
|
||||||
char buf[12];
|
char buf[32];
|
||||||
int i, n;
|
int i, n, m;
|
||||||
|
|
||||||
for (i = n = 0; i < Nval; i++) {
|
for (i = n = 0; i < Nval; i++) {
|
||||||
sprintf(buf, Fmt, Typp[i]);
|
m = sprintf(buf, Fmt, Typp[i]);
|
||||||
|
n = max(n, m);
|
||||||
n = max(n, (signed)strlen(buf));
|
|
||||||
} // endfor i
|
} // endfor i
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user