diff --git a/storage/connect/mysql-test/connect/r/pivot.result b/storage/connect/mysql-test/connect/r/pivot.result index 82b1e0a0b0b..4b39a21d3d9 100644 --- a/storage/connect/mysql-test/connect/r/pivot.result +++ b/storage/connect/mysql-test/connect/r/pivot.result @@ -229,7 +229,7 @@ DROP TABLE pets; # CREATE TABLE fruit ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, -`name` varchar(32) DEFAULT NULL, +`name` varchar(32) NOT NULL, `cnt` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin1; diff --git a/storage/connect/mysql-test/connect/t/pivot.test b/storage/connect/mysql-test/connect/t/pivot.test index 3212496a220..d26e6cec628 100644 --- a/storage/connect/mysql-test/connect/t/pivot.test +++ b/storage/connect/mysql-test/connect/t/pivot.test @@ -1,163 +1,163 @@ --- source include/not_embedded.inc - -let $MYSQLD_DATADIR= `select @@datadir`; -let $PORT= `select @@port`; ---copy_file $MTR_SUITE_DIR/std_data/expenses.txt $MYSQLD_DATADIR/test/expenses.txt - ---echo # ---echo # Testing the PIVOT table type ---echo # -CREATE TABLE expenses ( -Who CHAR(10) NOT NULL, -Week INT(2) NOT NULL, -What CHAR(12) NOT NULL, -Amount DOUBLE(8,2)) -ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='expenses.txt' ENDING=2; -SELECT * FROM expenses; - ---echo # ---echo # Pivoting from What ---echo # -CREATE TABLE pivex ( -Who CHAR(10) NOT NULL, -Week INT(2) NOT NULL, -Beer DOUBLE(8,2) FLAG=1, -Car DOUBLE(8,2) FLAG=1, -Food DOUBLE(8,2) FLAG=1) -ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=expenses; ---replace_result $PORT PORT ---eval ALTER TABLE pivex OPTION_LIST='port=$PORT' -SELECT * FROM pivex; - ---echo # ---echo # Restricting the columns in a Pivot Table ---echo # -ALTER TABLE pivex DROP COLUMN week; -SELECT * FROM pivex; - ---echo # ---echo # Using a source definition ---echo # -DROP TABLE pivex; -CREATE TABLE pivex ( -Who CHAR(10) NOT NULL, -Week INT(2) NOT NULL, -Beer DOUBLE(8,2) FLAG=1, -Car DOUBLE(8,2) FLAG=1, -Food DOUBLE(8,2) FLAG=1) -ENGINE=CONNECT TABLE_TYPE=PIVOT -SRCDEF='select who, week, what, sum(amount) as amount from expenses where week in (4,5) group by who, week, what'; ---replace_result $PORT PORT ---eval ALTER TABLE pivex OPTION_LIST='PivotCol=what,FncCol=amount,port=$PORT' -SELECT * FROM pivex; - ---echo # ---echo # Pivoting from Week ---echo # -DROP TABLE pivex; -CREATE TABLE pivex ( -Who CHAR(10) NOT NULL, -What CHAR(12) NOT NULL, -`3` DOUBLE(8,2) FLAG=1, -`4` DOUBLE(8,2) FLAG=1, -`5` DOUBLE(8,2) FLAG=1) -ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=expenses; ---replace_result $PORT PORT ---eval ALTER TABLE pivex OPTION_LIST='PivotCol=Week,port=$PORT' -SELECT * FROM pivex; - ---echo # ---echo # Using scalar functions and expresssions ---echo # -DROP TABLE pivex; -CREATE TABLE pivex ( -Who CHAR(10) NOT NULL, -What CHAR(12) NOT NULL, -First DOUBLE(8,2) FLAG=1, -Middle DOUBLE(8,2) FLAG=1, -Last DOUBLE(8,2) FLAG=1) -ENGINE=CONNECT TABLE_TYPE=PIVOT -SRCDEF='select who, what, case when week=3 then ''First'' when week=5 then ''Last'' else ''Middle'' end as wk, sum(amount) * 6.56 as amnt from expenses group by who, what, wk'; ---replace_result $PORT PORT ---eval ALTER TABLE pivex OPTION_LIST='PivotCol=wk,FncCol=amnt,port=$PORT' -SELECT * FROM pivex; -DROP TABLE pivex; -DROP TABLE expenses; - ---echo # ---echo # Make the PETS table ---echo # -CREATE TABLE pets ( -Name VARCHAR(12) NOT NULL, -Race CHAR(6) NOT NULL, -Number INT NOT NULL) ENGINE=MYISAM; -INSERT INTO pets VALUES('John','dog',2); -INSERT INTO pets VALUES('Bill','cat',1); -INSERT INTO pets VALUES('Mary','dog',1); -INSERT INTO pets VALUES('Mary','cat',1); -INSERT INTO pets VALUES('Lisbeth','rabbit',2); -INSERT INTO pets VALUES('Kevin','cat',2); -INSERT INTO pets VALUES('Kevin','bird',6); -INSERT INTO pets VALUES('Donald','dog',1); -INSERT INTO pets VALUES('Donald','fish',3); -SELECT * FROM pets; - ---echo # ---echo # Pivot the PETS table ---echo # -CREATE TABLE pivet ( -name VARCHAR(12) NOT NULL, -dog INT NOT NULL DEFAULT 0 FLAG=1, -cat INT NOT NULL DEFAULT 0 FLAG=1, -rabbit INT NOT NULL DEFAULT 0 FLAG=1, -bird INT NOT NULL DEFAULT 0 FLAG=1, -fish INT NOT NULL DEFAULT 0 FLAG=1) -ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=pets OPTION_LIST='PivotCol=race,groupby=1'; -SELECT * FROM pivet; -DROP TABLE pivet; - ---echo # ---echo # Testing the "data" column list ---echo # -CREATE TABLE pivet ( -name VARCHAR(12) NOT NULL, -dog INT NOT NULL DEFAULT 0 FLAG=1, -cat INT NOT NULL DEFAULT 0 FLAG=1) -ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=pets OPTION_LIST='PivotCol=race,groupby=1'; ---error ER_GET_ERRMSG -SELECT * FROM pivet; -ALTER TABLE pivet OPTION_LIST='PivotCol=race,groupby=1,accept=1'; -SELECT * FROM pivet; -DROP TABLE pivet; - ---echo # ---echo # Adding a "dump" column ---echo # -CREATE TABLE pivet ( -name VARCHAR(12) NOT NULL, -dog INT NOT NULL DEFAULT 0 FLAG=1, -cat INT NOT NULL DEFAULT 0 FLAG=1, -other INT NOT NULL DEFAULT 0 FLAG=2) -ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=pets OPTION_LIST='PivotCol=race,groupby=1'; -SELECT * FROM pivet; - -DROP TABLE pivet; -DROP TABLE pets; - ---echo # ---echo # MDEV-5734 ---echo # -CREATE TABLE fruit ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(32) DEFAULT NULL, - `cnt` int(11) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin1; -INSERT INTO fruit VALUES (1,'apple',1),(2,'banana',1),(3,'apple',2),(4,'cherry',4),(5,'durazno',2); -SELECT * FROM fruit; -CREATE TABLE fruit_pivot ENGINE=CONNECT TABLE_TYPE=pivot TABNAME=fruit; -SELECT * FROM fruit_pivot; - -DROP TABLE fruit_pivot; -DROP TABLE fruit; ---remove_file $MYSQLD_DATADIR/test/expenses.txt +-- source include/not_embedded.inc + +let $MYSQLD_DATADIR= `select @@datadir`; +let $PORT= `select @@port`; +--copy_file $MTR_SUITE_DIR/std_data/expenses.txt $MYSQLD_DATADIR/test/expenses.txt + +--echo # +--echo # Testing the PIVOT table type +--echo # +CREATE TABLE expenses ( +Who CHAR(10) NOT NULL, +Week INT(2) NOT NULL, +What CHAR(12) NOT NULL, +Amount DOUBLE(8,2)) +ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='expenses.txt' ENDING=2; +SELECT * FROM expenses; + +--echo # +--echo # Pivoting from What +--echo # +CREATE TABLE pivex ( +Who CHAR(10) NOT NULL, +Week INT(2) NOT NULL, +Beer DOUBLE(8,2) FLAG=1, +Car DOUBLE(8,2) FLAG=1, +Food DOUBLE(8,2) FLAG=1) +ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=expenses; +--replace_result $PORT PORT +--eval ALTER TABLE pivex OPTION_LIST='port=$PORT' +SELECT * FROM pivex; + +--echo # +--echo # Restricting the columns in a Pivot Table +--echo # +ALTER TABLE pivex DROP COLUMN week; +SELECT * FROM pivex; + +--echo # +--echo # Using a source definition +--echo # +DROP TABLE pivex; +CREATE TABLE pivex ( +Who CHAR(10) NOT NULL, +Week INT(2) NOT NULL, +Beer DOUBLE(8,2) FLAG=1, +Car DOUBLE(8,2) FLAG=1, +Food DOUBLE(8,2) FLAG=1) +ENGINE=CONNECT TABLE_TYPE=PIVOT +SRCDEF='select who, week, what, sum(amount) as amount from expenses where week in (4,5) group by who, week, what'; +--replace_result $PORT PORT +--eval ALTER TABLE pivex OPTION_LIST='PivotCol=what,FncCol=amount,port=$PORT' +SELECT * FROM pivex; + +--echo # +--echo # Pivoting from Week +--echo # +DROP TABLE pivex; +CREATE TABLE pivex ( +Who CHAR(10) NOT NULL, +What CHAR(12) NOT NULL, +`3` DOUBLE(8,2) FLAG=1, +`4` DOUBLE(8,2) FLAG=1, +`5` DOUBLE(8,2) FLAG=1) +ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=expenses; +--replace_result $PORT PORT +--eval ALTER TABLE pivex OPTION_LIST='PivotCol=Week,port=$PORT' +SELECT * FROM pivex; + +--echo # +--echo # Using scalar functions and expresssions +--echo # +DROP TABLE pivex; +CREATE TABLE pivex ( +Who CHAR(10) NOT NULL, +What CHAR(12) NOT NULL, +First DOUBLE(8,2) FLAG=1, +Middle DOUBLE(8,2) FLAG=1, +Last DOUBLE(8,2) FLAG=1) +ENGINE=CONNECT TABLE_TYPE=PIVOT +SRCDEF='select who, what, case when week=3 then ''First'' when week=5 then ''Last'' else ''Middle'' end as wk, sum(amount) * 6.56 as amnt from expenses group by who, what, wk'; +--replace_result $PORT PORT +--eval ALTER TABLE pivex OPTION_LIST='PivotCol=wk,FncCol=amnt,port=$PORT' +SELECT * FROM pivex; +DROP TABLE pivex; +DROP TABLE expenses; + +--echo # +--echo # Make the PETS table +--echo # +CREATE TABLE pets ( +Name VARCHAR(12) NOT NULL, +Race CHAR(6) NOT NULL, +Number INT NOT NULL) ENGINE=MYISAM; +INSERT INTO pets VALUES('John','dog',2); +INSERT INTO pets VALUES('Bill','cat',1); +INSERT INTO pets VALUES('Mary','dog',1); +INSERT INTO pets VALUES('Mary','cat',1); +INSERT INTO pets VALUES('Lisbeth','rabbit',2); +INSERT INTO pets VALUES('Kevin','cat',2); +INSERT INTO pets VALUES('Kevin','bird',6); +INSERT INTO pets VALUES('Donald','dog',1); +INSERT INTO pets VALUES('Donald','fish',3); +SELECT * FROM pets; + +--echo # +--echo # Pivot the PETS table +--echo # +CREATE TABLE pivet ( +name VARCHAR(12) NOT NULL, +dog INT NOT NULL DEFAULT 0 FLAG=1, +cat INT NOT NULL DEFAULT 0 FLAG=1, +rabbit INT NOT NULL DEFAULT 0 FLAG=1, +bird INT NOT NULL DEFAULT 0 FLAG=1, +fish INT NOT NULL DEFAULT 0 FLAG=1) +ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=pets OPTION_LIST='PivotCol=race,groupby=1'; +SELECT * FROM pivet; +DROP TABLE pivet; + +--echo # +--echo # Testing the "data" column list +--echo # +CREATE TABLE pivet ( +name VARCHAR(12) NOT NULL, +dog INT NOT NULL DEFAULT 0 FLAG=1, +cat INT NOT NULL DEFAULT 0 FLAG=1) +ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=pets OPTION_LIST='PivotCol=race,groupby=1'; +--error ER_GET_ERRMSG +SELECT * FROM pivet; +ALTER TABLE pivet OPTION_LIST='PivotCol=race,groupby=1,accept=1'; +SELECT * FROM pivet; +DROP TABLE pivet; + +--echo # +--echo # Adding a "dump" column +--echo # +CREATE TABLE pivet ( +name VARCHAR(12) NOT NULL, +dog INT NOT NULL DEFAULT 0 FLAG=1, +cat INT NOT NULL DEFAULT 0 FLAG=1, +other INT NOT NULL DEFAULT 0 FLAG=2) +ENGINE=CONNECT TABLE_TYPE=PIVOT TABNAME=pets OPTION_LIST='PivotCol=race,groupby=1'; +SELECT * FROM pivet; + +DROP TABLE pivet; +DROP TABLE pets; + +--echo # +--echo # MDEV-5734 +--echo # +CREATE TABLE fruit ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(32) NOT NULL, + `cnt` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin1; +INSERT INTO fruit VALUES (1,'apple',1),(2,'banana',1),(3,'apple',2),(4,'cherry',4),(5,'durazno',2); +SELECT * FROM fruit; +CREATE TABLE fruit_pivot ENGINE=CONNECT TABLE_TYPE=pivot TABNAME=fruit; +SELECT * FROM fruit_pivot; + +DROP TABLE fruit_pivot; +DROP TABLE fruit; +--remove_file $MYSQLD_DATADIR/test/expenses.txt diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h index 48acd4bf409..6d80b8207c5 100644 --- a/storage/connect/plgdbsem.h +++ b/storage/connect/plgdbsem.h @@ -291,6 +291,7 @@ enum OPVAL {OP_EQ = 1, /* Filtering operator = */ OP_CURDT = 113, /* Scalar function Op CurDate */ OP_NWEEK = 114, /* Scalar function Op Week number*/ OP_ROW = 115, /* Scalar function Op Row */ + OP_PREV = 116, /* Index operator Find Previous */ OP_SYSTEM = 200, /* Scalar function Op System */ OP_REMOVE = 201, /* Scalar function Op Remove */ OP_RENAME = 202, /* Scalar function Op Rename */ diff --git a/storage/connect/tabpivot.cpp b/storage/connect/tabpivot.cpp index 7cde2ab4cbd..b236d3c62cd 100644 --- a/storage/connect/tabpivot.cpp +++ b/storage/connect/tabpivot.cpp @@ -96,10 +96,21 @@ PIVAID::PIVAID(const char *tab, const char *src, const char *picol, PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) { char *query, *colname, buf[64]; - int ndif, nblin, w = 0; + int rc, ndif, nblin, w = 0; + bool b = false; PVAL valp; PCOLRES *pcrp, crp, fncrp = NULL; + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return NULL; + } // endif jump_level + + if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) { + goto err; + } // endif rc + if (!Tabsrc && Tabname) { // Locate the query query = (char*)PlugSubAlloc(g, NULL, strlen(Tabname) + 16); @@ -113,16 +124,17 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) // Open a MySQL connection for this table if (Myc.Open(g, Host, Database, User, Pwd, Port)) return NULL; + else + b = true; // Send the source command to MySQL - if (Myc.ExecSQL(g, query, &w) == RC_FX) { - Myc.Close(); - return NULL; - } // endif Exec + if (Myc.ExecSQL(g, query, &w) == RC_FX) + goto err; // We must have a storage query to get pivot column values Qryp = Myc.GetResult(g, true); Myc.Close(); + b = false; if (!Fncol) { for (crp = Qryp->Colresp; crp; crp = crp->Next) @@ -152,6 +164,11 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) // Prepare the column list for (pcrp = &Qryp->Colresp; crp = *pcrp; ) if (!stricmp(Picol, crp->Name)) { + if (crp->Nulls) { + sprintf(g->Message, "Pivot column %s cannot be nullable", Picol); + return NULL; + } // endif Nulls + Rblkp = crp->Kdata; *pcrp = crp->Next; } else if (!stricmp(Fncol, crp->Name)) { @@ -218,6 +235,12 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g) // We added ndif columns and removed 2 (picol and fncol) Qryp->Nbcol += (ndif - 2); return Qryp; + +err: + if (b) + Myc.Close(); + + return NULL; } // end of MakePivotColumns /***********************************************************************/ diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp index 133f4546a56..38917f1d5df 100755 --- a/storage/connect/xindex.cpp +++ b/storage/connect/xindex.cpp @@ -1574,6 +1574,46 @@ bool XINDEX::NextVal(bool eq) return (Cur_K == Num_K || (eq && neq <= Nval)); } // end of NextVal +/***********************************************************************/ +/* XINDEX: Find Cur_K and Val_K's of previous index entry. */ +/* Returns false if Ok, true if there are no more values. */ +/***********************************************************************/ +bool XINDEX::PrevVal(void) + { + int n, neq = Nk + 1, curk; + PXCOL kcp; + + if (Cur_K == 0) + return true; + else + curk = --Cur_K; + + for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) { + if (kcp->Kof) { + if (curk < kcp->Kof[kcp->Val_K]) + neq = n; + + } else { +#ifdef _DEBUG + assert(curk == kcp->Val_K -1); +#endif // _DEBUG + neq = n; + } // endif Kof + +#ifdef _DEBUG + assert(kcp->Val_K >= 0); +#endif // _DEBUG + + // If this is not a break... + if (neq > n) + break; // all previous columns have same value + + curk = --kcp->Val_K; // This is a break, get new column value + } // endfor kcp + + return false; + } // end of PrevVal + /***********************************************************************/ /* XINDEX: Fetch a physical or logical record. */ /***********************************************************************/ @@ -1870,6 +1910,25 @@ int XINDXS::GroupSize(void) : 1; } // end of GroupSize +/***********************************************************************/ +/* XINDXS: Find Cur_K and Val_K of previous index value. */ +/* Returns false if Ok, true if there are no more values. */ +/***********************************************************************/ +bool XINDXS::PrevVal(void) + { + if (--Cur_K < 0) + return true; + + if (Mul) { + if (Cur_K < Pof[To_KeyCol->Val_K]) + To_KeyCol->Val_K--; + + } else + To_KeyCol->Val_K = Cur_K; + + return false; + } // end of PrevVal + /***********************************************************************/ /* XINDXS: Find Cur_K and Val_K of next index value. */ /* If b is true next value must be equal to last one. */ @@ -1914,16 +1973,16 @@ int XINDXS::Fetch(PGLOBAL g) /* Table read through a sorted index. */ /*********************************************************************/ switch (Op) { - case OP_NEXT: // Read next + case OP_NEXT: // Read next if (NextVal(false)) return -1; // End of indexed file break; - case OP_FIRST: // Read first + case OP_FIRST: // Read first To_KeyCol->Val_K = Cur_K = 0; Op = OP_NEXT; break; - case OP_SAME: // Read next same + case OP_SAME: // Read next same #if defined(TRACE) // printf("looking for next same value\n"); #endif // TRACE @@ -1934,7 +1993,7 @@ int XINDXS::Fetch(PGLOBAL g) } // endif Mul break; - case OP_NXTDIF: // Read next dif + case OP_NXTDIF: // Read next dif if (++To_KeyCol->Val_K == Ndif) return -1; // End of indexed file @@ -1946,10 +2005,15 @@ int XINDXS::Fetch(PGLOBAL g) break; case OP_LAST: // Read first Cur_K = Num_K - 1; - To_KeyCol->Val_K = To_KeyCol->Kblp->GetNval() - 1; - Op = OP_NEXT; + To_KeyCol->Val_K = Ndif - 1; + Op = OP_PREV; break; - default: // Should OP_EQ + case OP_PREV: // Read previous + if (PrevVal()) + return -1; // End of indexed file + + break; + default: // Should be OP_EQ /*****************************************************************/ /* Look for the first key equal to the link column values */ /* and return its rank whithin the index table. */ diff --git a/storage/connect/xindex.h b/storage/connect/xindex.h index 2f7a29bc246..6ed0416bad5 100644 --- a/storage/connect/xindex.h +++ b/storage/connect/xindex.h @@ -198,6 +198,7 @@ class DllExport XXBASE : public CSORT, public BLOCK { virtual int MaxRange(void) {return 1;} virtual int Fetch(PGLOBAL g) = 0; virtual bool NextVal(bool eq) {return true;} + virtual bool PrevVal(void) {return true;} virtual int FastFind(int nk) = 0; virtual bool Reorder(PGLOBAL g) {return true;} virtual int Range(PGLOBAL g, int limit = 0, bool incl = true) @@ -209,22 +210,22 @@ class DllExport XXBASE : public CSORT, public BLOCK { protected: // Members PTDBASE Tbxp; // Points to calling table TDB - PXCOL To_KeyCol; // To KeyCol class list + PXCOL To_KeyCol; // To KeyCol class list MBLOCK Record; // Record allocation block int* &To_Rec; // We are using ftell, fseek int Cur_K; // Index of current record int Old_K; // Index of last record int Num_K; // Size of Rec_K pointer array - int Ndif; // Number of distinct values + int Ndif; // Number of distinct values int Bot; // Bottom of research index int Top; // Top of research index int Inf, Sup; // Used for block optimization - OPVAL Op; // Search operator + OPVAL Op; // Search operator bool Mul; // true if multiple bool Srtd; // true for sorted column int Val_K; // Index of current value - int Nblk; // Number of blocks - int Sblk; // Block size + int Nblk; // Number of blocks + int Sblk; // Block size int Thresh; // Thresh for sorting join indexes int ID; // Index ID number int Nth; // Nth constant to fetch @@ -263,6 +264,7 @@ class DllExport XINDEX : public XXBASE { virtual int ColMaxSame(PXCOL kp); virtual void Close(void); virtual bool NextVal(bool eq); + virtual bool PrevVal(void); virtual bool Make(PGLOBAL g, PIXDEF sxp); virtual bool SaveIndex(PGLOBAL g, PIXDEF sxp); virtual bool Reorder(PGLOBAL g); @@ -302,6 +304,7 @@ class DllExport XINDXS : public XINDEX { virtual int Fetch(PGLOBAL g); virtual int FastFind(int nk); virtual bool NextVal(bool eq); + virtual bool PrevVal(void); virtual int Range(PGLOBAL g, int limit = 0, bool incl = true); virtual int GroupSize(void);