diff --git a/sql/src/createindex.cpp b/sql/src/createindex.cpp index d1fce3a..0422909 100644 --- a/sql/src/createindex.cpp +++ b/sql/src/createindex.cpp @@ -515,7 +515,7 @@ RCODE SQLStatement::processCreateIndex( // Get the index name - index name must NOT already exist - if (RC_BAD( rc = getIndexName( FALSE, szIndexName, sizeof( szIndexName), + if (RC_BAD( rc = getIndexName( FALSE, NULL, szIndexName, sizeof( szIndexName), &uiIndexNameLen, &pIndex))) { goto Exit; diff --git a/sql/src/dropindex.cpp b/sql/src/dropindex.cpp index c8744d9..ce17048 100644 --- a/sql/src/dropindex.cpp +++ b/sql/src/dropindex.cpp @@ -238,7 +238,7 @@ RCODE SQLStatement::processDropIndex( void) // Get the index name - index name must exist - if (RC_BAD( rc = getIndexName( TRUE, szIndexName, sizeof( szIndexName), + if (RC_BAD( rc = getIndexName( TRUE, NULL, szIndexName, sizeof( szIndexName), &uiIndexNameLen, &pIndex))) { goto Exit; diff --git a/sql/src/fdict.cpp b/sql/src/fdict.cpp index 8aa3ba8..37f5ba8 100644 --- a/sql/src/fdict.cpp +++ b/sql/src/fdict.cpp @@ -2141,7 +2141,7 @@ RCODE F_Db::dictReadEncDefs( void) } if (RC_BAD( rc = pTableCursor->setupRange( this, SFLM_TBLNUM_ENCDEFS, - 1, FLM_MAX_UINT64, NULL, NULL, NULL))) + 1, FLM_MAX_UINT64, FALSE))) { goto Exit; } @@ -2298,7 +2298,7 @@ RCODE F_Db::dictReadTables( void) } if (RC_BAD( rc = pTableCursor->setupRange( this, SFLM_TBLNUM_TABLES, - 1, FLM_MAX_UINT64, NULL, NULL, NULL))) + 1, FLM_MAX_UINT64, FALSE))) { goto Exit; } @@ -2430,7 +2430,7 @@ RCODE F_Db::dictReadColumns( void) } if (RC_BAD( rc = pTableCursor->setupRange( this, SFLM_TBLNUM_COLUMNS, - 1, FLM_MAX_UINT64, NULL, NULL, NULL))) + 1, FLM_MAX_UINT64, FALSE))) { goto Exit; } @@ -2662,7 +2662,7 @@ RCODE F_Db::dictReadIndexes( void) } if (RC_BAD( rc = pTableCursor->setupRange( this, SFLM_TBLNUM_INDEXES, - 1, FLM_MAX_UINT64, NULL, NULL, NULL))) + 1, FLM_MAX_UINT64, FALSE))) { goto Exit; } @@ -3033,7 +3033,7 @@ RCODE F_Db::dictReadIndexComponents( void) } if (RC_BAD( rc = pTableCursor->setupRange( this, SFLM_TBLNUM_INDEX_COMPONENTS, - 1, FLM_MAX_UINT64, NULL, NULL, NULL))) + 1, FLM_MAX_UINT64, FALSE))) { goto Exit; } diff --git a/sql/src/flaimsql.h b/sql/src/flaimsql.h index f9b3a81..5c0b024 100644 --- a/sql/src/flaimsql.h +++ b/sql/src/flaimsql.h @@ -145,6 +145,8 @@ typedef enum SQL_ERR_DUPLICATE_ALIAS_NAME, ///< 65 = Table alias name specified multiple times in SELECT statement. SQL_ERR_TOO_MANY_TABLES, ///< 66 = Too many tables specified in SELECT statement. SQL_ERR_EXPECTING_BY, ///< 67 = Expecting "BY" keyword. + SQL_ERR_INDEX_NOT_DEFINED_FOR_TABLE,///< 68 = Index specified is not associated with the specified table. + SQL_ERR_EXPECTING_INDEX_OR_NOINDEX, ///< 69 = Expecting INDEX or NOINDEX keyword. // IMPORTANT NOTE: If new codes are added, please update gv_SQLParseErrors in fshell.cpp SQL_NUM_ERRORS @@ -1364,18 +1366,20 @@ typedef struct #define NE_SFLM_Q_BAD_SEARCH_STRING 0xE20C ///< 0xE20C = String in query criteria is bad. #define NE_SFLM_Q_COMPARE_OPERAND_TYPE_MISMATCH 0xE20D ///< 0xE20D = Types of operands in a comparison operator are incompatible, cannot be compared. #define NE_SFLM_Q_INVALID_OPERATOR 0xE20E ///< 0xE20E = Invalid operator in query expression. -#define NE_Q_INVALID_ROW_ID_VALUE 0xE20F ///< 0xE20F = Could not get ROW ID from constant in query expression. -#define NE_SFLM_Q_EXPECTING_LPAREN 0xE210 ///< 0xE210 = Expecting a left parenthesis in query expression. -#define NE_SFLM_Q_ILLEGAL_OPERATOR 0xE211 ///< 0xE211 = Illegal operator specified in query expression. -#define NE_SFLM_Q_ILLEGAL_COMPARE_RULES 0xE212 ///< 0xE212 = Illegal combination comparison rules specified in query expression. -#define NE_SFLM_Q_UNEXPECTED_COLUMN 0xE213 ///< 0xE213 = Not expecting a column name in query expression. -#define NE_SFLM_Q_UNEXPECTED_CONSTANT 0xE214 ///< 0xE214 = Not expecting a constant in query expression. -#define NE_SFLM_Q_UNEXPECTED_BOOLEAN 0xE215 ///< 0xE215 = Not expecting a boolean constant in query expression. -#define NE_SFLM_Q_AMBIGUOUS_COLUMN_NAME 0xE216 ///< 0xE216 = Column name specified in SELECT statement found in multiple tables - must specify table. -#define NE_SFLM_Q_INVALID_COLUMN_NAME 0xE217 ///< 0xE217 = Column name specified in SELECT statement not defined in the tables that were specified. -#define NE_SFLM_Q_UNDEFINED_TABLE_FOR_COLUMN 0xE218 ///< 0xE218 = Table name specified for a column in SELECT statement not defined in the tables that were specified. -#define NE_SFLM_Q_BAD_ORDER_BY_TABLE 0xE219 ///< 0xE219 = Table name specified in ORDER BY is not part of the query. -#define NE_SFLM_Q_DUP_COLUMN_IN_ORDER_BY 0xE21A ///< 0xE21A = Duplicate table.column name specified in ORDER BY part of query. +#define NE_SFLM_Q_EXPECTING_LPAREN 0xE20F ///< 0xE20F = Expecting a left parenthesis in query expression. +#define NE_SFLM_Q_ILLEGAL_OPERATOR 0xE210 ///< 0xE210 = Illegal operator specified in query expression. +#define NE_SFLM_Q_ILLEGAL_COMPARE_RULES 0xE211 ///< 0xE211 = Illegal combination comparison rules specified in query expression. +#define NE_SFLM_Q_UNEXPECTED_COLUMN 0xE212 ///< 0xE212 = Not expecting a column name in query expression. +#define NE_SFLM_Q_UNEXPECTED_CONSTANT 0xE213 ///< 0xE213 = Not expecting a constant in query expression. +#define NE_SFLM_Q_UNEXPECTED_BOOLEAN 0xE214 ///< 0xE214 = Not expecting a boolean constant in query expression. +#define NE_SFLM_Q_AMBIGUOUS_COLUMN_NAME 0xE215 ///< 0xE215 = Column name specified in SELECT statement found in multiple tables - must specify table. +#define NE_SFLM_Q_INVALID_COLUMN_NAME 0xE216 ///< 0xE216 = Column name specified in SELECT statement not defined in the tables that were specified. +#define NE_SFLM_Q_UNDEFINED_TABLE_FOR_COLUMN 0xE217 ///< 0xE217 = Table name specified for a column in SELECT statement not defined in the tables that were specified. +#define NE_SFLM_Q_BAD_ORDER_BY_TABLE 0xE218 ///< 0xE218 = Table name specified in ORDER BY is not part of the query. +#define NE_SFLM_Q_DUP_COLUMN_IN_ORDER_BY 0xE219 ///< 0xE219 = Duplicate table.column name specified in ORDER BY part of query. +#define NE_SFLM_Q_INDEX_NOT_ON_TABLE 0xE21A ///< 0xE21A = Index specified is not associated with the table specified in the query. +#define NE_SFLM_Q_INVALID_TABLE_FOR_INDEX 0xE21B ///< 0xE21B = Table specified an index is not part of the query. +#define NE_SFLM_Q_INVALID_INDEX 0xE21C ///< 0xE21C = Index that was specified is invalid. // Desc: NICI / Encryption Errors diff --git a/sql/src/flaimsys.h b/sql/src/flaimsys.h index ce24f31..233ef2b 100644 --- a/sql/src/flaimsys.h +++ b/sql/src/flaimsys.h @@ -949,6 +949,9 @@ public: { return( getDataPtr( getVector( uiElementNumber, VECT_SLOT_HAS_DATA))); } + + RCODE copyVector( + F_DataVector * pSrcVector); private: @@ -2675,12 +2678,13 @@ private: RCODE ixKeyCompare( F_Db * pDb, F_INDEX * pIndex, - F_DataVector * pSearchKey, - F_Row * pRow1, - F_Row * pRow2, FLMBOOL bCompareRowId, + F_DataVector * pSearchKey1, + F_Row * pRow1, const void * pvKey1, FLMUINT uiKeyLen1, + F_DataVector * pSearchKey2, + F_Row * pRow2, const void * pvKey2, FLMUINT uiKeyLen2, FLMINT * piCompare); @@ -2716,9 +2720,10 @@ public: FLMUINT uiKeyLen2, FLMINT * piCompare) { - return( ixKeyCompare( m_pDb, m_pIndex, m_pSearchKey, - m_pOldRow, m_pOldRow, m_bCompareRowId, - pvKey1, uiKeyLen1, pvKey2, uiKeyLen2, piCompare)); + return( ixKeyCompare( m_pDb, m_pIndex, m_bCompareRowId, + m_pSearchKey, m_pOldRow, pvKey1, uiKeyLen1, + m_pSearchKey, m_pOldRow, pvKey2, uiKeyLen2, + piCompare)); } FINLINE void setIxInfo( diff --git a/sql/src/flconvrt.cpp b/sql/src/flconvrt.cpp index a9a6afe..f56aa54 100644 --- a/sql/src/flconvrt.cpp +++ b/sql/src/flconvrt.cpp @@ -618,7 +618,7 @@ RCODE F_Db::rollOverDbKey( void) } if (RC_BAD( rc = pTableCursor->setupRange( this, SFLM_TBLNUM_ENCDEFS, - 1, FLM_MAX_UINT64, NULL, NULL, NULL))) + 1, FLM_MAX_UINT64, FALSE))) { goto Exit; } diff --git a/sql/src/flkeyret.cpp b/sql/src/flkeyret.cpp index e16c494..3985d9f 100644 --- a/sql/src/flkeyret.cpp +++ b/sql/src/flkeyret.cpp @@ -203,9 +203,10 @@ RCODE F_Db::keyRetrieve( FLMINT iTmpCmp; if (RC_BAD( rc = ixKeyCompare( this, pIndex, - pSearchKey, NULL, NULL, (uiIdMatchFlags == FLM_MATCH_ROW_ID) ? TRUE : FALSE, + pSearchKey, NULL, pucFoundKey, uiFoundKeyLen, + pSearchKey, NULL, pucSearchKey, uiSearchKeyLen, &iTmpCmp))) { goto Exit; diff --git a/sql/src/fscursor.cpp b/sql/src/fscursor.cpp index 7205264..a8fa73a 100644 --- a/sql/src/fscursor.cpp +++ b/sql/src/fscursor.cpp @@ -27,6 +27,12 @@ #include "flaimsys.h" #include "fscursor.h" +FSTATIC RCODE copyKey( + KEYPOS * pDestKey, + F_DataVector * pDestSrchKey, + KEYPOS * pSrcKey, + F_DataVector * pSrcSrchKey); + /**************************************************************************** Desc: ****************************************************************************/ @@ -75,6 +81,12 @@ void FSIndexCursor::resetCursor( void) m_bAtBOF = TRUE; m_bAtEOF = FALSE; m_bSetup = FALSE; + m_bDoRowMatch = FALSE; + m_bCanCompareOnKey = TRUE; + m_ui64Cost = 0; + m_ui64LeafBlocksBetween = 0; + m_ui64TotalRefs = 0; + m_bTotalsEstimated = FALSE; } /**************************************************************************** @@ -119,6 +131,115 @@ Exit: return( rc); } +/**************************************************************************** +Desc: Estimate the cost of going between the from and until key and + fetching every row. +****************************************************************************/ +RCODE FSIndexCursor::calculateCost( void) +{ + RCODE rc = NE_SFLM_OK; + F_Btree * pUntilBTree = NULL; + FLMINT iCompare; + + // Get a btree object + + if (RC_BAD( rc = gv_SFlmSysData.pBtPool->btpReserveBtree( &pUntilBTree))) + { + goto Exit; + } + + if (RC_OK( rc = setKeyPosition( m_pDb, TRUE, FALSE, + &m_fromExtKey, &m_fromKey, &m_curKey, TRUE, NULL, NULL, NULL))) + { + + // All keys between FROM and UNTIL may be gone. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE, + &m_untilExtKey, NULL, + m_curKey.ucKey, m_curKey.uiKeyLen, + &m_untilExtKey, NULL, + m_untilKey.ucKey, m_untilKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + if (iCompare <= 0) + { + KEYPOS tmpUntilKey; + + if (RC_OK( rc = pUntilBTree->btOpen( m_pDb, m_pLFile, + isAbsolutePositionable(), FALSE, + &m_ixCompare))) + { + + // Going forward so position is exclusive + + rc = setKeyPosition( m_pDb, FALSE, FALSE, + &m_untilExtKey, &m_untilKey, + &tmpUntilKey, FALSE, NULL, pUntilBTree, NULL); + } + } + else + { + rc = RC_SET( NE_SFLM_BOF_HIT); + m_bAtBOF = TRUE; + m_bAtEOF = FALSE; + } + } + else + { + if (rc == NE_SFLM_EOF_HIT) + { + m_bAtEOF = TRUE; + m_bAtBOF = FALSE; + } + else if (rc == NE_SFLM_BOF_HIT) + { + m_bAtEOF = FALSE; + m_bAtBOF = TRUE; + } + } + + if (RC_BAD( rc)) + { + + // Empty tree or empty set case. + + if (rc == NE_SFLM_EOF_HIT || rc == NE_SFLM_BOF_HIT) + { + m_ui64Cost = 0; + m_ui64LeafBlocksBetween = 0; + m_ui64TotalRefs = 0; + m_bTotalsEstimated = FALSE; + rc = NE_SFLM_OK; + } + goto Exit; + } + else + { + if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree, + &m_ui64LeafBlocksBetween, &m_ui64TotalRefs, + &m_bTotalsEstimated, + (m_pDb->m_pDatabase->m_uiBlockSize * 3) / 4))) + { + goto Exit; + } + if ((m_ui64Cost = m_ui64LeafBlocksBetween + m_ui64TotalRefs) == 0) + { + m_ui64Cost = 1; + } + } + +Exit: + + if (pUntilBTree) + { + gv_SFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree); + } + + return( rc); +} + /**************************************************************************** Desc: Setup the from and until keys in the cursor. Return counts after positioning to the from and until key in the index. @@ -127,17 +248,9 @@ RCODE FSIndexCursor::setupKeys( F_Db * pDb, F_INDEX * pIndex, F_TABLE * pTable, - SQL_PRED ** ppKeyComponents, - FLMUINT uiComponentsUsed, - FLMBOOL * pbDoRowMatch, - FLMBOOL * pbCanCompareOnKey, - FLMUINT64 * pui64LeafBlocksBetween, // [out] blocks between the stacks - FLMUINT64 * pui64TotalRefs, // [out] total references - FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. + SQL_PRED ** ppKeyComponents) { - RCODE rc = NE_SFLM_OK; - F_Btree * pUntilBTree = NULL; - FLMINT iCompare; + RCODE rc = NE_SFLM_OK; m_uiIndexNum = pIndex->uiIndexNum; @@ -147,10 +260,10 @@ RCODE FSIndexCursor::setupKeys( } if (RC_BAD( rc = flmBuildFromAndUntilKeys( pDb->getDict(), pIndex, pTable, - ppKeyComponents, uiComponentsUsed, + ppKeyComponents, &m_fromExtKey, m_fromKey.ucKey, &m_fromKey.uiKeyLen, &m_untilExtKey, m_untilKey.ucKey, &m_untilKey.uiKeyLen, - pbDoRowMatch, pbCanCompareOnKey))) + &m_bDoRowMatch, &m_bCanCompareOnKey))) { goto Exit; } @@ -158,111 +271,287 @@ RCODE FSIndexCursor::setupKeys( m_curKey.uiKeyLen = 0; m_bSetup = TRUE; - // Want any of the counts back? - - if (pui64LeafBlocksBetween || pui64TotalRefs) + if (RC_BAD( rc = calculateCost())) { - - // Get a btree object - - if (RC_BAD( rc = gv_SFlmSysData.pBtPool->btpReserveBtree( &pUntilBTree))) - { - goto Exit; - } - - if (RC_OK( rc = setKeyPosition( pDb, TRUE, FALSE, - &m_fromExtKey, &m_fromKey, &m_curKey, TRUE, NULL, NULL, NULL))) - { - - // All keys between FROM and UNTIL may be gone. - - if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, &m_untilExtKey, - NULL, NULL, FALSE, - m_curKey.ucKey, m_curKey.uiKeyLen, - m_untilKey.ucKey, m_untilKey.uiKeyLen, - &iCompare))) - { - goto Exit; - } - if (iCompare <= 0) - { - KEYPOS tmpUntilKey; - - if (RC_OK( rc = pUntilBTree->btOpen( pDb, m_pLFile, - isAbsolutePositionable(), FALSE, - &m_ixCompare))) - { - - // Going forward so position is exclusive - - rc = setKeyPosition( pDb, FALSE, FALSE, - &m_untilExtKey, &m_untilKey, - &tmpUntilKey, FALSE, NULL, pUntilBTree, NULL); - } - } - else - { - rc = RC_SET( NE_SFLM_BOF_HIT); - m_bAtBOF = TRUE; - m_bAtEOF = FALSE; - } - } - else - { - if (rc == NE_SFLM_EOF_HIT) - { - m_bAtEOF = TRUE; - m_bAtBOF = FALSE; - } - else if (rc == NE_SFLM_BOF_HIT) - { - m_bAtEOF = FALSE; - m_bAtBOF = TRUE; - } - } - - if (RC_BAD( rc)) - { - - // Empty tree or empty set case. - - if (rc == NE_SFLM_EOF_HIT || rc == NE_SFLM_BOF_HIT) - { - if (pui64LeafBlocksBetween) - { - *pui64LeafBlocksBetween = 0; - } - if (pui64TotalRefs) - { - *pui64TotalRefs = 0; - } - if (pbTotalsEstimated) - { - *pbTotalsEstimated = FALSE; - } - rc = NE_SFLM_OK; - } - goto Exit; - } - else - { - if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree, - pui64LeafBlocksBetween, pui64TotalRefs, - pbTotalsEstimated, - (pDb->m_pDatabase->m_uiBlockSize * 3) / 4))) - { - goto Exit; - } - } + goto Exit; } m_bAtBOF = TRUE; Exit: - if (pUntilBTree) + return( rc); +} + +/**************************************************************************** +Desc: Set the destination key equal to the source key. +****************************************************************************/ +FSTATIC RCODE copyKey( + KEYPOS * pDestKey, + F_DataVector * pDestSrchKey, + KEYPOS * pSrcKey, + F_DataVector * pSrcSrchKey) +{ + RCODE rc = NE_SFLM_OK; + + // Copy the key buffer. + + if ((pDestKey->uiKeyLen = pSrcKey->uiKeyLen) != 0) { - gv_SFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree); + f_memcpy( pDestKey->ucKey, pSrcKey->ucKey, pSrcKey->uiKeyLen); } + + // Copy the data vector. + + if (RC_BAD( rc = pDestSrchKey->copyVector( pSrcSrchKey))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Union the keys from another index cursor into this one. +****************************************************************************/ +RCODE FSIndexCursor::unionKeys( + F_Db * pDb, + FSIndexCursor * pFSIndexCursor, + FLMBOOL * pbUnioned, + FLMINT * piCompare) +{ + RCODE rc = NE_SFLM_OK; + FLMINT iCompare; + FLMBOOL bIncomingLessThan = FALSE; + FLMBOOL bIncomingGreaterThan = FALSE; + + *pbUnioned = FALSE; + + // Make sure both cursors are referencing the same index. If not, + // they cannot be unioned. + + if (pFSIndexCursor->m_uiIndexNum != m_uiIndexNum) + { + goto Exit; // Will return NE_SFLM_OK + } + + // Both cursors better already be setup + + flmAssert( m_bSetup && pFSIndexCursor->m_bSetup); + + // Set both cursors to have their m_pDb, m_pIndex, etc. set up. + // These are needed for comparison of keys. + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + if (RC_BAD( rc = pFSIndexCursor->checkTransaction( pDb))) + { + goto Exit; + } + + // Compare the two from keys. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE, + &pFSIndexCursor->m_fromExtKey, NULL, + pFSIndexCursor->m_fromKey.ucKey, + pFSIndexCursor->m_fromKey.uiKeyLen, + &m_fromExtKey, NULL, + m_fromKey.ucKey, + m_fromKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + + if (iCompare < 0) + { + + // If the incoming from key is less than our from key, + // see if the incoming until key is >= our from key. + // If so, they overlap. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE, + &pFSIndexCursor->m_untilExtKey, NULL, + pFSIndexCursor->m_untilKey.ucKey, + pFSIndexCursor->m_untilKey.uiKeyLen, + &m_fromExtKey, NULL, + m_fromKey.ucKey, + m_fromKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + + if (iCompare < 0) + { + + // Incoming until key is less than our from key - no union. + + *piCompare = -1; + goto Exit; + } + + // Incoming from key is < our from key, and incoming until key + // is >= our from key - definitely an overlap, and we need to + // set our from key equal to the incoming from key. + + *pbUnioned = TRUE; + if (RC_BAD( rc = copyKey( &m_fromKey, &m_fromExtKey, + &pFSIndexCursor->m_fromKey, + &pFSIndexCursor->m_fromExtKey))) + { + goto Exit; + } + bIncomingLessThan = TRUE; + + // Setup so that we will have to reposition on a call to + // firstKey or nextKey. + + m_curKey.uiKeyLen = 0; + m_bAtBOF = TRUE; + + // If the incoming until key was > our from key, see if it is + // also greater than our until key. If so, our until key will + // be set to be equal to the incoming until key. + + if (iCompare > 0) + { + goto Compare_Until_Keys; + } + } + else + { + + if (iCompare > 0) + { + // The incoming from key is > than our from key, + // see if the incoming from key is > our until key there + // is no overlap. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE, + &pFSIndexCursor->m_fromExtKey, NULL, + pFSIndexCursor->m_fromKey.ucKey, + pFSIndexCursor->m_fromKey.uiKeyLen, + &m_untilExtKey, NULL, + m_untilKey.ucKey, + m_untilKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + + if (iCompare > 0) + { + + // Incoming from key is also greater than our until key - no overlap. + + *piCompare = 1; + goto Exit; + } + } + + // From key is between our from and until key - definitely a union case. + // Just need to see if the incoming until key is > our until key. + // If so, set our until key to be equal to the incoming until key. + + *pbUnioned = TRUE; + +Compare_Until_Keys: + + // Compare the until keys. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE, + &pFSIndexCursor->m_untilExtKey, NULL, + pFSIndexCursor->m_untilKey.ucKey, + pFSIndexCursor->m_untilKey.uiKeyLen, + &m_untilExtKey, NULL, + m_untilKey.ucKey, + m_untilKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + if (iCompare > 0) + { + + // Incoming until key is > our until key, set our until key to be + // equal to the incoming until key. + + if (RC_BAD( rc = copyKey( &m_untilKey, &m_untilExtKey, + &pFSIndexCursor->m_untilKey, + &pFSIndexCursor->m_untilExtKey))) + { + goto Exit; + } + bIncomingGreaterThan = TRUE; + } + // else iCompare <= 0 + // Incoming until key is <= our until key - no need to change our + // until key. + + } + + // If we need to recalculate the cost, do so here. + + if (bIncomingLessThan && bIncomingGreaterThan) + { + + // Our range was a complete subset of the incoming range, so + // we should be able to just use whatever cost was calculated + // for it, unless no cost was calculated, in which case we + // need to calculate a cost. + + if (pFSIndexCursor->m_ui64Cost) + { + m_ui64Cost = pFSIndexCursor->m_ui64Cost; + m_ui64LeafBlocksBetween = pFSIndexCursor->m_ui64LeafBlocksBetween; + m_ui64TotalRefs = pFSIndexCursor->m_ui64TotalRefs; + m_bTotalsEstimated = pFSIndexCursor->m_bTotalsEstimated; + } + else + { + if (RC_BAD( rc = calculateCost())) + { + goto Exit; + } + } + } + + // If we have not calculated a cost, or the union created a larger + // range of keys on the from or until side, then we need to + // recalculate the cost. + + else if (bIncomingLessThan || bIncomingGreaterThan || !m_ui64Cost) + { + if (RC_BAD( rc = calculateCost())) + { + goto Exit; + } + } + + // If we unioned the keys, set the do row match and can compare on key + // flags to the worst case. If the incoming cursor required us to + // match on the row, then we need to set this cursor to match on the row. + // If the incoming cursor did not allow us to compare on the key, then + // we don't want to allow this cursor to compare on the key. + + if (*pbUnioned) + { + if (pFSIndexCursor->m_bDoRowMatch) + { + m_bDoRowMatch = TRUE; + } + if (!pFSIndexCursor->m_bCanCompareOnKey) + { + m_bCanCompareOnKey = FALSE; + } + + } + +Exit: return( rc); } @@ -457,10 +746,11 @@ RCODE FSIndexCursor::setKeyPosition( if (!bExcludeKey) { - if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, pExtSrchKey, - NULL, NULL, + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, pExtSrchKey ? FALSE : TRUE, + pExtSrchKey, NULL, pFoundKey->ucKey, pFoundKey->uiKeyLen, + pExtSrchKey, NULL, pSearchKey->ucKey, pSearchKey->uiKeyLen, &iCompare))) { @@ -601,9 +891,10 @@ RCODE FSIndexCursor::checkIfKeyInRange( // It is already guaranteed to be >= the fromKey, we just need to // make sure it is <= the until key. - if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, &m_untilExtKey, - NULL, NULL, FALSE, + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE, + &m_untilExtKey, NULL, m_curKey.ucKey, m_curKey.uiKeyLen, + &m_untilExtKey, NULL, m_untilKey.ucKey, m_untilKey.uiKeyLen, &iCompare))) { @@ -624,9 +915,10 @@ RCODE FSIndexCursor::checkIfKeyInRange( // It is already guaranteed to be <= the untilKey, we just need to // make sure it is >= the from key. - if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, &m_fromExtKey, - NULL, NULL, FALSE, + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIndex, FALSE, + &m_fromExtKey, NULL, m_curKey.ucKey, m_curKey.uiKeyLen, + &m_fromExtKey, NULL, m_fromKey.ucKey, m_fromKey.uiKeyLen, &iCompare))) { @@ -819,8 +1111,10 @@ Check_Key: // If the bSkipCurrKey flag is TRUE, we want to skip keys until // we come to one where the key part is different. - if (RC_BAD( rc = ixKeyCompare( pDb, m_pIndex, NULL, NULL, NULL, FALSE, + if (RC_BAD( rc = ixKeyCompare( pDb, m_pIndex, FALSE, + NULL, NULL, m_curKey.ucKey, m_curKey.uiKeyLen, + NULL, NULL, saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen, &iCompare))) { @@ -1008,9 +1302,10 @@ Check_Key: // If the bSkipCurrKey flag is TRUE, we want to skip keys until // we come to one where the key part is different. - - if (RC_BAD( rc = ixKeyCompare( pDb, m_pIndex, NULL, NULL, NULL, FALSE, + if (RC_BAD( rc = ixKeyCompare( pDb, m_pIndex, FALSE, + NULL, NULL, m_curKey.ucKey, m_curKey.uiKeyLen, + NULL, NULL, saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen, &iCompare))) { diff --git a/sql/src/fscursor.h b/sql/src/fscursor.h index 33b94da..3a5158f 100644 --- a/sql/src/fscursor.h +++ b/sql/src/fscursor.h @@ -49,17 +49,19 @@ public: RCODE resetTransaction( F_Db * pDb); + RCODE calculateCost( void); + RCODE setupKeys( F_Db * pDb, F_INDEX * pIndex, F_TABLE * pTable, - SQL_PRED ** ppKeyComponents, - FLMUINT uiComponentsUsed, - FLMBOOL * pbDoRowMatch, - FLMBOOL * pbCanCompareOnKey, - FLMUINT64 * pui64LeafBlocksBetween, - FLMUINT64 * pui64TotalRefs, - FLMBOOL * pbTotalsEstimated); + SQL_PRED ** ppKeyComponents); + + RCODE unionKeys( + F_Db * pDb, + FSIndexCursor * pFSIndexCursor, + FLMBOOL * pbUnioned, + FLMINT * piCompare); RCODE currentKey( F_Db * pDb, @@ -83,6 +85,21 @@ public: F_DataVector * pKey, FLMBOOL bSkipCurrKey); + FINLINE FLMUINT64 getCost( void) + { + return( m_ui64Cost); + } + + FINLINE FLMBOOL doRowMatch( void) + { + return( m_bDoRowMatch); + } + + FINLINE FLMBOOL canCompareOnKey( void) + { + return( m_bCanCompareOnKey); + } + private: RCODE useNewDb( @@ -168,6 +185,12 @@ private: FLMBOOL m_bSetup; KEYPOS m_fromKey; KEYPOS m_untilKey; + FLMUINT64 m_ui64Cost; + FLMUINT64 m_ui64LeafBlocksBetween; + FLMUINT64 m_ui64TotalRefs; + FLMBOOL m_bTotalsEstimated; + FLMBOOL m_bDoRowMatch; + FLMBOOL m_bCanCompareOnKey; // State information. @@ -208,9 +231,7 @@ public: FLMUINT uiTableNum, FLMUINT64 ui64LowRowId, FLMUINT64 ui64HighRowId, - FLMUINT64 * pui64LeafBlocksBetween, - FLMUINT64 * pui64TotalRows, - FLMBOOL * pbTotalsEstimated); + FLMBOOL bEstimateCost); RCODE currentRow( F_Db * pDb, @@ -236,6 +257,11 @@ public: F_Db * pDb, F_Row ** ppRow, FLMUINT64 * pui64RowId); + + FINLINE FLMUINT64 getCost( void) + { + return( m_ui64Cost); + } private: @@ -304,6 +330,10 @@ private: FLMBOOL m_bSetup; FLMUINT64 m_ui64FromRowId; FLMUINT64 m_ui64UntilRowId; + FLMUINT64 m_ui64Cost; + FLMUINT64 m_ui64LeafBlocksBetween; + FLMUINT64 m_ui64TotalRows; + FLMBOOL m_bTotalsEstimated; // State information. @@ -321,7 +351,6 @@ RCODE flmBuildFromAndUntilKeys( F_INDEX * pIndex, F_TABLE * pTable, SQL_PRED ** ppKeyComponents, - FLMUINT uiComponentsUsed, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, diff --git a/sql/src/fsdatacu.cpp b/sql/src/fsdatacu.cpp index 845efc7..83f97a3 100644 --- a/sql/src/fsdatacu.cpp +++ b/sql/src/fsdatacu.cpp @@ -281,9 +281,7 @@ RCODE FSTableCursor::setupRange( FLMUINT uiTableNum, FLMUINT64 ui64LowRowId, FLMUINT64 ui64HighRowId, - FLMUINT64 * pui64LeafBlocksBetween, // [out] blocks between the stacks - FLMUINT64 * pui64TotalRows, // [out] - FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. + FLMBOOL bEstimateCost) { RCODE rc = NE_SFLM_OK; F_Btree * pUntilBTree = NULL; @@ -303,20 +301,8 @@ RCODE FSTableCursor::setupRange( // Want any of the counts back? - if (pui64LeafBlocksBetween || pui64TotalRows) + if (bEstimateCost) { - if (pui64LeafBlocksBetween) - { - *pui64LeafBlocksBetween = 0; - } - if (pui64TotalRows) - { - *pui64TotalRows = 0; - } - if (pbTotalsEstimated) - { - *pbTotalsEstimated = FALSE; - } // Position to the FROM and UNTIL key so we can get the stats. @@ -325,6 +311,10 @@ RCODE FSTableCursor::setupRange( { if (rc == NE_SFLM_EOF_HIT) { + m_ui64Cost = 0; + m_ui64LeafBlocksBetween = 0; + m_ui64TotalRows = 0; + m_bTotalsEstimated = FALSE; rc = NE_SFLM_OK; } goto Exit; @@ -357,13 +347,31 @@ RCODE FSTableCursor::setupRange( goto Exit; } if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree, - pui64LeafBlocksBetween, pui64TotalRows, - pbTotalsEstimated, + &m_ui64LeafBlocksBetween, &m_ui64TotalRows, + &m_bTotalsEstimated, (pDb->m_pDatabase->m_uiBlockSize * 3) / 4))) { goto Exit; } + if ((m_ui64Cost = m_ui64LeafBlocksBetween) == 0) + { + m_ui64Cost = 1; + } } + else + { + m_ui64Cost = 0; + m_ui64LeafBlocksBetween = 0; + m_ui64TotalRows = 0; + m_bTotalsEstimated = FALSE; + } + } + else + { + m_ui64Cost = 0; + m_ui64LeafBlocksBetween = 0; + m_ui64TotalRows = 0; + m_bTotalsEstimated = FALSE; } Exit: diff --git a/sql/src/fslfileu.cpp b/sql/src/fslfileu.cpp index a0191e3..353163e 100644 --- a/sql/src/fslfileu.cpp +++ b/sql/src/fslfileu.cpp @@ -863,7 +863,7 @@ Retry: pTableCursor->resetCursor(); if (RC_BAD( rc = pTableCursor->setupRange( pDb, SFLM_TBLNUM_BLOCK_CHAINS, - 1, FLM_MAX_UINT64, NULL, NULL, NULL))) + 1, FLM_MAX_UINT64, FALSE))) { goto Exit; } diff --git a/sql/src/fvector.cpp b/sql/src/fvector.cpp index 0084329..2c43abe 100644 --- a/sql/src/fvector.cpp +++ b/sql/src/fvector.cpp @@ -1353,3 +1353,65 @@ Exit: return( rc); } +/**************************************************************************** +Desc: Copy one data vector into another. +****************************************************************************/ +RCODE F_DataVector::copyVector( + F_DataVector * pSrcVector) +{ + RCODE rc = NE_SFLM_OK; + + m_ui64RowId = pSrcVector->m_ui64RowId; + + // If none of the elements are populated, there is nothing that + // needs to be done. + + if ((m_uiNumElements = pSrcVector->m_uiNumElements) > 0) + { + if (RC_BAD( rc = allocVectorArray( pSrcVector->m_uiNumElements - 1))) + { + goto Exit; + } + f_memcpy( m_pVectorElements, pSrcVector->m_pVectorElements, + sizeof( F_VECTOR_ELEMENT) * m_uiNumElements); + + // Make sure we have enough room in our data buffer. + + if (pSrcVector->m_uiDataBufOffset > m_uiDataBufLength) + { + if (m_pucDataBuf == &m_ucIntDataBuf [0]) + { + if (RC_BAD( rc = f_alloc( pSrcVector->m_uiDataBufOffset, + &m_pucDataBuf))) + { + goto Exit; + } + + } + else + { + if (RC_BAD( rc = f_realloc( pSrcVector->m_uiDataBufOffset, + &m_pucDataBuf))) + { + goto Exit; + } + } + m_uiDataBufLength = pSrcVector->m_uiDataBufOffset; + } + + // Copy the source data buffer into ours. + + m_uiDataBufOffset = pSrcVector->m_uiDataBufOffset; + f_memcpy( m_pucDataBuf, pSrcVector->m_pucDataBuf, + pSrcVector->m_uiDataBufOffset); + } + else + { + m_uiDataBufOffset = 0; + } + +Exit: + + return( rc); +} + diff --git a/sql/src/kybldkey.cpp b/sql/src/kybldkey.cpp index 45dd521..de11f55 100644 --- a/sql/src/kybldkey.cpp +++ b/sql/src/kybldkey.cpp @@ -610,7 +610,7 @@ FSTATIC RCODE flmAddNonTextKeyPiece( // routines can do a comparison on the full value if // necessary. - if (RC_BAD( rc = pFromSearchKey->setBinary( uiKeyComponent - 1, + if (RC_BAD( rc = pFromSearchKey->setBinary( uiKeyComponent, pucFromBuf, uiFromBufLen))) { goto Exit; @@ -696,7 +696,7 @@ FSTATIC RCODE flmAddNonTextKeyPiece( // routines can do a comparison on the full value if // necessary. - if (RC_BAD( rc = pUntilSearchKey->setBinary( uiKeyComponent - 1, + if (RC_BAD( rc = pUntilSearchKey->setBinary( uiKeyComponent, pucUntilBuf, uiUntilBufLen))) { goto Exit; @@ -1571,7 +1571,7 @@ FSTATIC RCODE flmAddTextKeyPiece( // routines can do a comparison on the full value if // necessary. - if (RC_BAD( rc = pFromSearchKey->setUTF8( uiKeyComponent - 1, + if (RC_BAD( rc = pFromSearchKey->setUTF8( uiKeyComponent, pucFromUTF8Buf, uiFromBufLen))) { goto Exit; @@ -1671,7 +1671,7 @@ FSTATIC RCODE flmAddTextKeyPiece( // routines can do a comparison on the full value if // necessary. - if (RC_BAD( rc = pUntilSearchKey->setUTF8( uiKeyComponent - 1, + if (RC_BAD( rc = pUntilSearchKey->setUTF8( uiKeyComponent, pucUntilUTF8Buf, uiUntilBufLen))) { goto Exit; @@ -1809,7 +1809,7 @@ FSTATIC RCODE flmAddTextKeyPiece( // routines can do a comparison on the full value if // necessary. - if (RC_BAD( rc = pUntilSearchKey->setUTF8( uiKeyComponent - 1, + if (RC_BAD( rc = pUntilSearchKey->setUTF8( uiKeyComponent, pucUntilUTF8Buf, uiUntilBufLen))) { goto Exit; @@ -1874,7 +1874,6 @@ RCODE flmBuildFromAndUntilKeys( F_INDEX * pIndex, F_TABLE * pTable, SQL_PRED ** ppKeyComponents, - FLMUINT uiComponentsUsed, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, @@ -1897,7 +1896,7 @@ RCODE flmBuildFromAndUntilKeys( *pbDoRowMatch = FALSE; *pbCanCompareOnKey = TRUE; - if (!uiComponentsUsed) + if (!ppKeyComponents) { // Setup a first-to-last key @@ -1911,18 +1910,20 @@ RCODE flmBuildFromAndUntilKeys( } else { - for (uiKeyComponent = 1, pIcd = pIndex->pKeyIcds; - uiKeyComponent <= uiComponentsUsed; + for (uiKeyComponent = 0, pIcd = pIndex->pKeyIcds; + uiKeyComponent < pIndex->uiNumKeyComponents; uiKeyComponent++, pIcd++) { + if ((pPred = ppKeyComponents [uiKeyComponent]) == NULL) + { + break; + } // At this point, we always better have room to put at least // two bytes in the key. flmAssert( SFLM_MAX_KEY_SIZE - uiFromKeyLen >= 2 && SFLM_MAX_KEY_SIZE - uiUntilKeyLen >= 2); - - pPred = ppKeyComponents [uiKeyComponent - 1]; // Predicates we are looking at should NEVER be notted. They // will have been weeded out earlier. @@ -1964,7 +1965,8 @@ RCODE flmBuildFromAndUntilKeys( // component is inclusive, in which case we need to add one // more component to the key so it will be "inclusive". - if (uiKeyComponent == uiComponentsUsed || + if (uiKeyComponent + 1 == pIndex->uiNumKeyComponents || + !ppKeyComponents [uiKeyComponent + 1] || SFLM_MAX_KEY_SIZE - uiFromKeyLen < 2 || SFLM_MAX_KEY_SIZE - uiUntilKeyLen < 2) { @@ -1975,7 +1977,7 @@ RCODE flmBuildFromAndUntilKeys( // two byte value. if (bFromIncl && - uiKeyComponent < pIndex->uiNumKeyComponents && + uiKeyComponent + 1 < pIndex->uiNumKeyComponents && SFLM_MAX_KEY_SIZE - uiFromKeyLen >= 2) { UW2FBA( (FLMUINT16)KEY_LOW_VALUE, &pucFromKey [uiFromKeyLen]); @@ -1988,7 +1990,7 @@ RCODE flmBuildFromAndUntilKeys( if (bUntilIncl) { - if (uiKeyComponent < pIndex->uiNumKeyComponents) + if (uiKeyComponent + 1 < pIndex->uiNumKeyComponents) { if (SFLM_MAX_KEY_SIZE - uiUntilKeyLen >= 2) { diff --git a/sql/src/kyqsort.cpp b/sql/src/kyqsort.cpp index 927b637..2c7720b 100644 --- a/sql/src/kyqsort.cpp +++ b/sql/src/kyqsort.cpp @@ -331,11 +331,12 @@ Desc: Compares result set entries during the finalization stage to allow RCODE ixKeyCompare( F_Db * pDb, F_INDEX * pIndex, - F_DataVector * pSearchKey, - F_Row * pRow1, - F_Row * pRow2, FLMBOOL bCompareRowId, + F_DataVector * pSearchKey1, + F_Row * pRow1, const void * pvKey1, + F_DataVector * pSearchKey2, + F_Row * pRow2, FLMUINT uiKeyLen1, const void * pvKey2, FLMUINT uiKeyLen2, @@ -488,14 +489,14 @@ RCODE ixKeyCompare( if (isSearchKeyComponent( pucKey1)) { - flmAssert( pSearchKey); - ui64RowId1 = pSearchKey->getRowId(); + flmAssert( pSearchKey1); + ui64RowId1 = pSearchKey1->getRowId(); // The search key better have a row ID or the untruncated // value. flmAssert( ui64RowId1 || - !pSearchKey->isRightTruncated( uiKeyComponent)); + !pSearchKey1->isRightTruncated( uiKeyComponent)); } else { @@ -511,14 +512,14 @@ RCODE ixKeyCompare( if (isSearchKeyComponent( pucKey2)) { - flmAssert( pSearchKey); - ui64RowId2 = pSearchKey->getRowId(); + flmAssert( pSearchKey2); + ui64RowId2 = pSearchKey2->getRowId(); // The search key better have a row ID or the untruncated // value. flmAssert( ui64RowId2 || - !pSearchKey->isRightTruncated( uiKeyComponent)); + !pSearchKey2->isRightTruncated( uiKeyComponent)); } else { @@ -550,13 +551,13 @@ RCODE ixKeyCompare( { if (RC_BAD( rc = ixKeyGetUTF8( pDb, pIndex->uiTableNum, pIcd, pRow1, ui64RowId1, - pSearchKey, uiKeyComponent, &dynaBuf1))) + pSearchKey1, uiKeyComponent, &dynaBuf1))) { goto Exit; } if (RC_BAD( rc = ixKeyGetUTF8( pDb, pIndex->uiTableNum, pIcd, pRow2, ui64RowId2, - pSearchKey, uiKeyComponent, &dynaBuf2))) + pSearchKey2, uiKeyComponent, &dynaBuf2))) { goto Exit; } @@ -589,13 +590,13 @@ RCODE ixKeyCompare( { if (RC_BAD( rc = ixKeyGetBinary( pDb, pIndex->uiTableNum, pIcd, pRow1, ui64RowId1, - pSearchKey, uiKeyComponent, &dynaBuf1))) + pSearchKey1, uiKeyComponent, &dynaBuf1))) { goto Exit; } if (RC_BAD( rc = ixKeyGetBinary( pDb, pIndex->uiTableNum, pIcd, pRow2, ui64RowId2, - pSearchKey, uiKeyComponent, &dynaBuf2))) + pSearchKey2, uiKeyComponent, &dynaBuf2))) { goto Exit; } @@ -785,9 +786,10 @@ FINLINE RCODE krefCompareIxAndKey( pIndex = pDb->getDict()->getIndex( (FLMUINT)pKrefA->ui16IxNum); } - if (RC_BAD( rc = ixKeyCompare( pDb, pIndex, NULL, - pKrefA->pRow, pKrefB->pRow, TRUE, + if (RC_BAD( rc = ixKeyCompare( pDb, pIndex, TRUE, + NULL, pKrefA->pRow, &pKrefA [1], (FLMUINT)pKrefA->ui16KeyLen, + NULL, pKrefB->pRow, &pKrefB [1], (FLMUINT)pKrefB->ui16KeyLen, piCompare))) { goto Exit; diff --git a/sql/src/select.cpp b/sql/src/select.cpp index 61fc84e..a82fffe 100644 --- a/sql/src/select.cpp +++ b/sql/src/select.cpp @@ -154,6 +154,7 @@ RCODE SQLStatement::processSelect( void) FLMUINT uiTokenLen; FLMBOOL bDistinct; F_TABLE * pTable; + F_INDEX * pIndex; SELECT_EXPR * pFirstSelectExpr = NULL; SELECT_EXPR * pLastSelectExpr = NULL; SELECT_EXPR * pSelectExpr; @@ -258,6 +259,8 @@ RCODE SQLStatement::processSelect( void) // Add the table name to the list + tableList [uiNumTables].bScan = FALSE; + tableList [uiNumTables].uiIndexNum = 0; tableList [uiNumTables].uiTableNum = pTable->uiTableNum; tableList [uiNumTables].pszTableAlias = NULL; uiNumTables++; @@ -285,9 +288,59 @@ RCODE SQLStatement::processSelect( void) bHaveOrderBy = TRUE; break; } + else if (f_stricmp( szToken, "using") == 0) + { +Get_Index: + if (RC_BAD( rc = getToken( szToken, sizeof( szToken), FALSE, + &uiTokenLineOffset, &uiTokenLen))) + { + goto Exit; + } + if (f_stricmp( szToken, "noindex") == 0) + { + tableList [uiNumTables - 1].bScan = TRUE; + tableList [uiNumTables - 1].uiIndexNum = 0; + } + else if (f_stricmp( szToken, "index") == 0) + { + + // Get the index name. + + if (RC_BAD( rc = getIndexName( TRUE, pTable, szToken, sizeof( szToken), + &uiTokenLen, &pIndex))) + { + goto Exit; + } + tableList [uiNumTables - 1].bScan = FALSE; + tableList [uiNumTables - 1].uiIndexNum = pIndex->uiIndexNum; + } + else + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_EXPECTING_INDEX_OR_NOINDEX, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } else if ((szToken [0] >= 'a' && szToken [0] <= 'z') || (szToken [0] >= 'A' && szToken [0] <= 'Z')) { + + // If token is the keyword "as", it must be followed by the + // alias name. + + if (f_stricmp( szToken, "as") == 0) + { + if (RC_BAD( rc = getName( szToken, sizeof( szToken), + &uiTokenLen, &uiTokenLineOffset))) + { + goto Exit; + } + } + // See if this alias name has been used in the table list already. for (uiLoop = 0; uiLoop < uiNumTables; uiLoop++) @@ -315,7 +368,40 @@ RCODE SQLStatement::processSelect( void) } f_memcpy( &tableList [uiNumTables - 1].pszTableAlias, szToken, uiTokenLen + 1); - + if (RC_BAD( rc = getToken( szToken, sizeof( szToken), TRUE, + &uiTokenLineOffset, &uiTokenLen))) + { + if (rc == NE_SFLM_EOF_HIT) + { + rc = NE_SFLM_OK; + break; + } + goto Exit; + } + if (f_stricmp( szToken, "where") == 0) + { + bHaveWhere = TRUE; + break; + } + else if (f_stricmp( szToken, "order") == 0) + { + bHaveOrderBy = TRUE; + break; + } + else if (f_stricmp( szToken, "using") == 0) + { + goto Get_Index; + } + else if (f_stricmp( szToken, ",") != 0) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_EXPECTING_COMMA, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } } else if (f_stricmp( szToken, ",") != 0) { @@ -353,6 +439,21 @@ RCODE SQLStatement::processSelect( void) { goto Exit; } + if (tableList [uiLoop].bScan) + { + if (RC_BAD( rc = sqlQuery.setIndex( tableList [uiLoop].uiTableNum, 0))) + { + goto Exit; + } + } + else if (tableList [uiLoop].uiIndexNum) + { + if (RC_BAD( rc = sqlQuery.setIndex( tableList [uiLoop].uiTableNum, + tableList [uiLoop].uiIndexNum))) + { + goto Exit; + } + } } // If there is a WHERE clause, parse it. diff --git a/sql/src/sqloptimize.cpp b/sql/src/sqloptimize.cpp index 36a0624..bffb3a5 100644 --- a/sql/src/sqloptimize.cpp +++ b/sql/src/sqloptimize.cpp @@ -24,6 +24,8 @@ #include "flaimsys.h" +#define MINIMUM_COST_ESTIMATE 8 + // Local function prototypes FSTATIC RCODE sqlGetRowIdValue( @@ -77,7 +79,9 @@ FSTATIC FLMBOOL predIsForOnlyThisTable( SQL_TABLE * pSQLTable); FSTATIC void rankIndexes( - SQL_TABLE * pSQLTable); + F_Db * pDb, + SQL_INDEX ** ppFirstSQLIndex, + SQL_INDEX ** ppLastSQLIndex); //------------------------------------------------------------------------- // Desc: Get the row ID constant from an SQL_VALUE node. @@ -105,7 +109,7 @@ FSTATIC RCODE sqlGetRowIdValue( pValue->val.ui64Val = (FLMUINT64)(pValue->val.i64Val); break; default: - rc = RC_SET_AND_ASSERT( NE_Q_INVALID_ROW_ID_VALUE); + rc = RC_SET_AND_ASSERT( NE_SFLM_Q_INVALID_ROW_ID_VALUE); goto Exit; } @@ -1940,18 +1944,20 @@ FSTATIC FLMBOOL predIsForOnlyThisTable( // with respect to a particular table. //------------------------------------------------------------------------- RCODE SQLQuery::getPredKeys( - SQL_PRED * pPred, - SQL_TABLE * pSQLTable) + F_TABLE * pTable, + FLMUINT uiForceIndexNum, + SQL_PRED * pPred, + SQL_INDEX ** ppFirstSQLIndex, + SQL_INDEX ** ppLastSQLIndex) { RCODE rc = NE_SFLM_OK; ICD * pIcd; SQL_INDEX * pSQLIndex; SQL_KEY * pKey; - F_TABLE * pTable = m_pDb->m_pDict->getTable( pPred->pSQLTable->uiTableNum); F_COLUMN * pColumn = m_pDb->m_pDict->getColumn( pTable, pPred->uiColumnNum); F_INDEX * pIndex; FLMUINT uiKeyComponent; - + // This ICD chain will only contain ICDs for this particular column on // the table the column belongs to. @@ -1961,7 +1967,7 @@ RCODE SQLQuery::getPredKeys( // If the table has an index specified for it, skip this ICD if // it is not that index. - if (pSQLTable->bIndexSet && pSQLTable->uiIndexNum != pIcd->uiIndexNum) + if (uiForceIndexNum && uiForceIndexNum != pIcd->uiIndexNum) { continue; } @@ -1976,7 +1982,7 @@ RCODE SQLQuery::getPredKeys( // Find the index off of the table. If not there, add it. - pSQLIndex = pSQLTable->pFirstSQLIndex; + pSQLIndex = *ppFirstSQLIndex; while (pSQLIndex->uiIndexNum != pIcd->uiIndexNum) { pSQLIndex = pSQLIndex->pNext; @@ -1988,18 +1994,16 @@ RCODE SQLQuery::getPredKeys( { goto Exit; } - pSQLIndex->pSQLTable = pSQLTable; pSQLIndex->uiIndexNum = pIcd->uiIndexNum; - pSQLIndex->uiNumComponents = pIndex->uiNumKeyComponents; - if ((pSQLIndex->pPrev = pSQLTable->pLastSQLIndex) != NULL) + if ((pSQLIndex->pPrev = *ppLastSQLIndex) != NULL) { pSQLIndex->pPrev->pNext = pSQLIndex; } else { - pSQLTable->pFirstSQLIndex = pSQLIndex; + *ppFirstSQLIndex = pSQLIndex; } - pSQLTable->pLastSQLIndex = pSQLIndex; + *ppLastSQLIndex = pSQLIndex; // Allocate a single key for the index. @@ -2008,20 +2012,20 @@ RCODE SQLQuery::getPredKeys( { goto Exit; } - pSQLIndex->pLastKey = pSQLIndex->pFirstKey = pKey; - pKey->pSQLIndex = pSQLIndex; + pSQLIndex->pLastSQLKey = pSQLIndex->pFirstSQLKey = pKey; // Allocate an array of key components for the key. - if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_PRED *) * pSQLIndex->uiNumComponents, - (void **)&pKey->ppKeyComponents))) + if (RC_BAD( rc = m_pool.poolCalloc( + sizeof( SQL_PRED *) * pIndex->uiNumKeyComponents, + (void **)&pKey->ppPredicates))) { goto Exit; } } else { - pKey = pSQLIndex->pFirstKey; + pKey = pSQLIndex->pFirstSQLKey; } // There should not be multiple predicates in a sub-query that @@ -2029,8 +2033,8 @@ RCODE SQLQuery::getPredKeys( // be populated. uiKeyComponent = (FLMUINT)(pIcd - pIndex->pKeyIcds); - flmAssert( !pKey->ppKeyComponents [uiKeyComponent]); - pKey->ppKeyComponents [uiKeyComponent] = pPred; + flmAssert( !pKey->ppPredicates [uiKeyComponent]); + pKey->ppPredicates [uiKeyComponent] = pPred; // NOTE: Costs will be calculated later. @@ -2042,26 +2046,29 @@ Exit: } //------------------------------------------------------------------------- -// Desc: Determine the order in which to evaluate indexes for a particular -// table and sub-query. Those that have a primary key will be -// give preference over those that don't. +// Desc: Determine the order in which to evaluate indexes. Those that have +// a primary key will be given preference over those that don't. //------------------------------------------------------------------------- FSTATIC void rankIndexes( - SQL_TABLE * pSQLTable) + F_Db * pDb, + SQL_INDEX ** ppFirstSQLIndex, + SQL_INDEX ** ppLastSQLIndex) { SQL_INDEX * pSQLIndex; SQL_INDEX * pPrevSQLIndex; SQL_INDEX * pNextSQLIndex; SQL_KEY * pKey; + F_INDEX * pIndex; - pSQLIndex = pSQLTable->pFirstSQLIndex; + pSQLIndex = *ppFirstSQLIndex; while (pSQLIndex) { pNextSQLIndex = pSQLIndex->pNext; pPrevSQLIndex = pSQLIndex->pPrev; // There should only be one key off of the index right now. - pKey = pSQLIndex->pFirstKey; + + pKey = pSQLIndex->pFirstSQLKey; flmAssert( !pKey->pNext); // Determine how many of the key's components point to a @@ -2069,9 +2076,10 @@ FSTATIC void rankIndexes( // be pointers after that one, but we really don't care, because // we won't use those components to generate a key. + pIndex = pDb->getDict()->getIndex( pSQLIndex->uiIndexNum); pKey->uiComponentsUsed = 0; - while (pKey->uiComponentsUsed < pSQLIndex->uiNumComponents && - pKey->ppKeyComponents [pKey->uiComponentsUsed]) + while (pKey->uiComponentsUsed < pIndex->uiNumKeyComponents && + pKey->ppPredicates [pKey->uiComponentsUsed]) { pKey->uiComponentsUsed++; } @@ -2081,7 +2089,7 @@ FSTATIC void rankIndexes( while (pPrevSQLIndex) { - if (pKey->uiComponentsUsed > pPrevSQLIndex->pFirstKey->uiComponentsUsed) + if (pKey->uiComponentsUsed > pPrevSQLIndex->pFirstSQLKey->uiComponentsUsed) { // Move our current key up in front of the previous key - meaning // it will be evaluated ahead of that key. @@ -2097,7 +2105,7 @@ FSTATIC void rankIndexes( } else { - pSQLTable->pLastSQLIndex = pSQLIndex->pPrev; + *ppLastSQLIndex = pSQLIndex->pPrev; } // Now, link it in front of pPrevSQLIndex @@ -2109,7 +2117,7 @@ FSTATIC void rankIndexes( } else { - pSQLTable->pFirstSQLIndex = pSQLIndex; + *ppFirstSQLIndex = pSQLIndex; } pPrevSQLIndex->pPrev = pSQLIndex; pPrevSQLIndex = pSQLIndex->pPrev; @@ -2126,67 +2134,235 @@ FSTATIC void rankIndexes( //------------------------------------------------------------------------- // Desc: Choose the best index for a table of the indexes for which we have -// generated predicate keys. +// generated predicate keys. All other indexes for the table will +// be freed //------------------------------------------------------------------------- RCODE SQLQuery::chooseBestIndex( - SQL_TABLE * pSQLTable, - FLMUINT64 * //pui64Cost - VISIT: NEED TO FINISH THIS ROUTINE + F_TABLE * pTable, + SQL_INDEX * pFirstSQLIndex, + SQL_INDEX ** ppBestSQLIndex ) { - RCODE rc = NE_SFLM_OK; - SQL_INDEX * pSQLIndex = pSQLTable->pFirstSQLIndex; + RCODE rc = NE_SFLM_OK; + SQL_INDEX * pBestSQLIndex = NULL; + SQL_KEY * pSQLKey; + FSIndexCursor * pFSIndexCursor = NULL; + F_INDEX * pIndex; - while (pSQLIndex) + while (pFirstSQLIndex) { // Should only be one key on each index at this point. - flmAssert( pSQLIndex->pFirstKey && pSQLIndex->pFirstKey == pSQLIndex->pLastKey); + flmAssert( pFirstSQLIndex->pFirstSQLKey && + pFirstSQLIndex->pFirstSQLKey == pFirstSQLIndex->pLastSQLKey); - pSQLIndex = pSQLIndex->pNext; + pSQLKey = pFirstSQLIndex->pFirstSQLKey; + + pIndex = m_pDb->m_pDict->getIndex( pFirstSQLIndex->uiIndexNum); + flmAssert( pIndex); + + // Allocate an index cursor, if necessary. + + if (!pFSIndexCursor) + { + if ((pFSIndexCursor = f_new FSIndexCursor) == NULL) + { + rc = RC_SET( NE_SFLM_MEM); + goto Exit; + } + } + else + { + pFSIndexCursor->resetCursor(); + } + + // Setup from and until keys and calculate the cost. + + if (RC_BAD( rc = pFSIndexCursor->setupKeys( m_pDb, pIndex, pTable, + pSQLKey->ppPredicates))) + { + goto Exit; + } + + // See if this index has a lower cost than our current best index. + + if (!pBestSQLIndex || + pFSIndexCursor->getCost() < pBestSQLIndex->pFirstSQLKey->pFSIndexCursor->getCost()) + { + pFirstSQLIndex->pFirstSQLKey->pFSIndexCursor = pFSIndexCursor; + + // If we have a best index, keep its index cursor so we can just + // reset it in the loop above rather than having to free it and + // allocate another one - a little optimization. + + if (!pBestSQLIndex) + { + pFSIndexCursor = NULL; + } + else + { + pFSIndexCursor = pBestSQLIndex->pFirstSQLKey->pFSIndexCursor; + } + pBestSQLIndex = pFirstSQLIndex; + + // If our best index's cost is low enough, no need to check any other + // indexes. + + if (pFSIndexCursor->getCost() < MINIMUM_COST_ESTIMATE) + { + break; + } + } + + pFirstSQLIndex = pFirstSQLIndex->pNext; } -// VISIT - need to finish up this routine +Exit: -//Exit: - VISIT: WILL USE THIS LABEL PROBABLY WHEN THIS ROUTINE IS FINISHED + if (pFSIndexCursor) + { + pFSIndexCursor->Release(); + } + *ppBestSQLIndex = pBestSQLIndex; return( rc); } //------------------------------------------------------------------------- -// Desc: Calculate the cost of doing a table scan for a table. +// Desc: Merge keys from pSQLIndex into the list of indexes for pSQLTable. //------------------------------------------------------------------------- -RCODE SQLQuery::calcTableScanCost( - SQL_TABLE * pSQLTable, - FLMUINT64 * pui64Cost, - FSTableCursor ** ppFSTableCursor) +RCODE SQLQuery::mergeKeys( + SQL_TABLE * pSQLTable, + SQL_INDEX * pSQLIndex) { RCODE rc = NE_SFLM_OK; - FLMUINT64 ui64LeafBlocksBetween; - FLMUINT64 ui64TotalRows; - FLMBOOL bTotalsEstimated; + SQL_INDEX * pFoundSQLIndex; - if ((*ppFSTableCursor = f_new FSTableCursor) == NULL) + // Should only be one key for this index. + + flmAssert( pSQLIndex->pFirstSQLKey == pSQLIndex->pLastSQLKey); + + // See if there is an index for the table that matches this one. There + // should only be one. + + pFoundSQLIndex = pSQLTable->pFirstSQLIndex; + while (pFoundSQLIndex && pFoundSQLIndex->uiIndexNum != pSQLIndex->uiIndexNum) { - rc = RC_SET( NE_SFLM_MEM); - goto Exit; + pFoundSQLIndex = pFoundSQLIndex->pNext; } - if (RC_BAD( rc = (*ppFSTableCursor)->setupRange( m_pDb, - pSQLTable->uiTableNum, 1, FLM_MAX_UINT64, - &ui64LeafBlocksBetween, &ui64TotalRows, - &bTotalsEstimated))) + + if (!pFoundSQLIndex) { - (*ppFSTableCursor)->Release(); - *ppFSTableCursor = NULL; - goto Exit; - } - if (!ui64LeafBlocksBetween) - { - *pui64Cost = 1; + + // Did not find a matching index. + // Just link the index at the end of the table's list of indexes. + // NOTE: It really doesn't matter if it is linked in at the beginning + // or the end or the middle somewhere as long as it gets into the + // list. + + if ((pSQLIndex->pPrev = pSQLTable->pLastSQLIndex) != NULL) + { + pSQLIndex->pPrev->pNext = pSQLIndex; + } + else + { + pSQLTable->pFirstSQLIndex = pSQLIndex; + } + pSQLTable->pLastSQLIndex = pSQLIndex; + + // Increment the total cost for the table + + pSQLTable->ui64TotalCost += pSQLIndex->pFirstSQLKey->pFSIndexCursor->getCost(); } else { - *pui64Cost = ui64LeafBlocksBetween; + FLMUINT ui64SaveCost; + FLMBOOL bUnioned = FALSE; + SQL_KEY * pSQLKey; + SQL_KEY * pIncomingSQLKey = pSQLIndex->pFirstSQLKey; + FSIndexCursor * pMergeFromFSIndexCursor = pIncomingSQLKey->pFSIndexCursor; + FLMINT iCompare; + + // Found a matching index, see if we can union the incoming index's + // key with one of the existing keys for the found index. + // If not, we will simply link the new key into the list. + + pSQLKey = pFoundSQLIndex->pFirstSQLKey; + while (pSQLKey) + { + ui64SaveCost = pSQLKey->pFSIndexCursor->getCost(); + + if (RC_BAD( rc = pSQLKey->pFSIndexCursor->unionKeys( m_pDb, + pMergeFromFSIndexCursor, &bUnioned, &iCompare))) + { + goto Exit; + } + if (bUnioned) + { + pSQLTable->ui64TotalCost -= ui64SaveCost; + pSQLTable->ui64TotalCost += pSQLKey->pFSIndexCursor->getCost(); + break; + } + else if (iCompare < 0) + { + + // The incoming key does not overlap the current key, and is + // less than it, so we will link it in before the current key. + + pIncomingSQLKey->pNext = pSQLKey; + if ((pIncomingSQLKey->pPrev = pSQLKey->pPrev) != NULL) + { + pSQLKey->pPrev->pNext = pIncomingSQLKey; + } + else + { + pFoundSQLIndex->pFirstSQLKey = pIncomingSQLKey; + } + pSQLKey->pPrev = pIncomingSQLKey; + + // We didn't really union, but we did link it into the list, + // so we set this flag to keep us from linking it at the end + // of the list outside this loop. + + bUnioned = TRUE; + break; + } + pSQLKey = pSQLKey->pNext; + } + + // If we did not union with any of the existing keys, this incoming + // key is greater than all of the keys in the index (otherwise it + // would have been linked into the list in the above loop). Hence, + // we link it in at the end of the list of keys for the found index. + // The keys in the list are deliberately kept in ascending order + // so that we can traverse through the index in order if need be. + + if (!bUnioned) + { + + // Link the incoming key as the last key off of the found index. + + pSQLKey = pSQLIndex->pFirstSQLKey; + if ((pSQLKey->pPrev = pFoundSQLIndex->pLastSQLKey) != NULL) + { + pSQLKey->pPrev->pNext = pSQLKey; + } + else + { + pFoundSQLIndex->pFirstSQLKey = pSQLKey; + } + pFoundSQLIndex->pLastSQLKey = pSQLKey; + } + + // Probably don't need to do this, because we should be getting + // rid of the SQL_INDEX structure pointed to by pSQLIndex (the caller + // should not use it anymore), but this it keeps us from having + // two SQL_INDEX structures point to the same key - just in case + // something on the outside changes. + + pSQLIndex->pFirstSQLKey = NULL; + pSQLIndex->pLastSQLKey = NULL; } Exit: @@ -2194,19 +2370,6 @@ Exit: return( rc); } -//------------------------------------------------------------------------- -// Desc: Merge keys from pSrcTable into pDestTable. -//------------------------------------------------------------------------- -RCODE SQLQuery::mergeKeys( - SQL_TABLE * pDestSQLTable, - SQL_TABLE * pSrcSQLTable) -{ -// VISIT: NEED TO FINISH THIS ROUTINE - (void)pDestSQLTable; - (void)pSrcSQLTable; - return( NE_SFLM_OK); -} - //------------------------------------------------------------------------- // Desc: Optimize a particular table for a particular sub-query. //------------------------------------------------------------------------- @@ -2215,22 +2378,19 @@ RCODE SQLQuery::optimizeTable( SQL_TABLE * pSQLTable) { RCODE rc = NE_SFLM_OK; - SQL_TABLE tmpSQLTable; + F_TABLE * pTable = m_pDb->m_pDict->getTable( pSQLTable->uiTableNum); + SQL_INDEX * pFirstSQLIndex = NULL; + SQL_INDEX * pLastSQLIndex = NULL; + SQL_INDEX * pSQLIndex; FLMUINT uiLoop; SQL_NODE * pOperand; void * pvMark = m_pool.poolMark(); - FSTableCursor * pFSTableCursor = NULL; // This routine should not be called if the table has already been // marked to do a table scan. flmAssert( !pSQLTable->bScan); - f_memset( &tmpSQLTable, 0, sizeof( SQL_TABLE)); - tmpSQLTable.uiTableNum = pSQLTable->uiTableNum; - tmpSQLTable.uiIndexNum = pSQLTable->uiIndexNum; - tmpSQLTable.bIndexSet = pSQLTable->bIndexSet; - // Traverse the predicates of the sub-query. If any are found // that are not predicates, the table must be scanned. @@ -2252,9 +2412,12 @@ RCODE SQLQuery::optimizeTable( if (predIsForOnlyThisTable( pOperand, pSQLTable)) { + if (RC_BAD( rc = setupTableScan( pSQLTable))) + { + goto Exit; + } + freeTableIndexes( pSQLTable); m_pool.poolReset( pvMark); - tmpSQLTable.pFirstSQLIndex = NULL; - tmpSQLTable.pLastSQLIndex = NULL; break; } } @@ -2269,9 +2432,12 @@ RCODE SQLQuery::optimizeTable( if ((pPred->bNotted && pPred->eOperator == SQL_MATCH_OP) || pPred->eOperator == SQL_NE_OP) { + if (RC_BAD( rc = setupTableScan( pSQLTable))) + { + goto Exit; + } + freeTableIndexes( pSQLTable); m_pool.poolReset( pvMark); - tmpSQLTable.pFirstSQLIndex = NULL; - tmpSQLTable.pLastSQLIndex = NULL; break; } @@ -2279,102 +2445,42 @@ RCODE SQLQuery::optimizeTable( // For now we are just collecting them. We will calculate // the best one later. - if (RC_BAD( rc = getPredKeys( pPred, &tmpSQLTable))) + if (RC_BAD( rc = getPredKeys( pTable, pSQLTable->uiIndexNum, + pPred, &pFirstSQLIndex, &pLastSQLIndex))) { goto Exit; } } } - // If we didn't find indexes for this table, set the bScan flag. + // If we found indexes for the table, choose the best one. - if (!tmpSQLTable.pFirstSQLIndex) - { - tmpSQLTable.bScan = TRUE; - if (RC_BAD( rc = calcTableScanCost( &tmpSQLTable, &tmpSQLTable.ui64Cost, - &pFSTableCursor))) - { - goto Exit; - } - } - else + if (pFirstSQLIndex) { // Rank the indexes to determine which ones to estimate cost for // first. - rankIndexes( &tmpSQLTable); + rankIndexes( m_pDb, &pFirstSQLIndex, &pLastSQLIndex); - // Find the index with the lowest cost, if any. - // If the lowest cost index is still high, estimate the cost of doing - // a table scan. + // Find the index with the lowest cost. - if (RC_BAD( rc = chooseBestIndex( &tmpSQLTable, &tmpSQLTable.ui64Cost))) + if (RC_BAD( rc = chooseBestIndex( pTable, pFirstSQLIndex, &pSQLIndex))) { goto Exit; } - // Should be one index left after this. If the cost is high, see if - // a table scan would be cheaper. - - if (tmpSQLTable.ui64Cost > 8) - { - FLMUINT64 ui64ScanCost; - - if (RC_BAD( rc = calcTableScanCost( &tmpSQLTable, &ui64ScanCost, - &pFSTableCursor))) - { - goto Exit; - } - if (ui64ScanCost < tmpSQLTable.ui64Cost) - { - m_pool.poolReset( pvMark); - tmpSQLTable.ui64Cost = ui64ScanCost; - tmpSQLTable.bScan = TRUE; - tmpSQLTable.pFirstSQLIndex = NULL; - tmpSQLTable.pLastSQLIndex = NULL; - } - else - { - pFSTableCursor->Release(); - pFSTableCursor = NULL; - } - } - } - - // If we determined that we must do a table scan, set the bScan flag - // for the master table. Otherwise, merge these keys - - if (tmpSQLTable.bScan) - { - pSQLTable->bScan = TRUE; - pSQLTable->ui64Cost = tmpSQLTable.ui64Cost; + // Merge the selected key for selected index into the keys for the + // master table. - // Better have calculated a cost and have a collection - // cursor at this point. - - flmAssert( pFSTableCursor); - pSQLTable->pFSTableCursor = pFSTableCursor; - pFSTableCursor = NULL; - pSQLTable->pFirstSQLIndex = NULL; - pSQLTable->pLastSQLIndex = NULL; - } - else - { - if (RC_BAD( rc = mergeKeys( pSQLTable, &tmpSQLTable))) + if (RC_BAD( rc = mergeKeys( pSQLTable, pSQLIndex))) { goto Exit; } - pSQLTable->ui64Cost += tmpSQLTable.ui64Cost; } Exit: - if (pFSTableCursor) - { - pFSTableCursor->Release(); - } - return( rc); } @@ -2383,56 +2489,88 @@ Exit: //------------------------------------------------------------------------- RCODE SQLQuery::optimizeSubQueries( void) { - RCODE rc = NE_SFLM_OK; - SQL_SUBQUERY * pSubQuery; - SQL_TABLE * pSQLTable; + RCODE rc = NE_SFLM_OK; + SQL_SUBQUERY * pSubQuery; + SQL_TABLE * pSQLTable; // For each table in our expression, attempt to pick an index for each // subquery. for (pSQLTable = m_pFirstSQLTable; pSQLTable; pSQLTable = pSQLTable->pNext) { - pSubQuery = m_pFirstSubQuery; - while (pSubQuery) + if (pSQLTable->bScan) { - if (RC_BAD( rc = optimizeTable( pSubQuery, pSQLTable))) + if (RC_BAD( rc = setupTableScan( pSQLTable))) { goto Exit; } - - // If the optimization decided we should scan the table, there - // is no need to look at any more sub-queries for this table. - - if (pSQLTable->bScan) - { - break; - } - pSubQuery = pSubQuery->pNext; } - - // See if a table scan is going to be cheaper. - - if (!pSQLTable->bScan && pSQLTable->ui64Cost > 8) + else { - FSTableCursor * pFSTableCursor = NULL; - FLMUINT64 ui64ScanCost; + pSubQuery = m_pFirstSubQuery; + while (pSubQuery) + { + if (RC_BAD( rc = optimizeTable( pSubQuery, pSQLTable))) + { + goto Exit; + } + + // If the optimization decided we should scan the table, there + // is no need to look at any more sub-queries for this table. + + if (pSQLTable->bScan) + { + break; + } + pSubQuery = pSubQuery->pNext; + } - if (RC_BAD( rc = calcTableScanCost( pSQLTable, &ui64ScanCost, - &pFSTableCursor))) + // If the table's cost is still zero, that means it was not optimized for + // any of the sub-queries, so we need to have it do a table scan or + // an index scan. + + if (!pSQLTable->ui64TotalCost) { - goto Exit; + if (pSQLTable->uiIndexNum) + { + if (RC_BAD( rc = setupIndexScan( pSQLTable))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = setupTableScan( pSQLTable))) + { + goto Exit; + } + } } - if (ui64ScanCost < pSQLTable->ui64Cost) + else if (!pSQLTable->bScan && + pSQLTable->ui64TotalCost > MINIMUM_COST_ESTIMATE) { - pSQLTable->ui64Cost = ui64ScanCost; - pSQLTable->bScan = TRUE; - pSQLTable->pFSTableCursor = pFSTableCursor; - pSQLTable->pFirstSQLIndex = NULL; - pSQLTable->pLastSQLIndex = NULL; - } - else - { - pFSTableCursor->Release(); + FLMUINT64 ui64SaveCost = pSQLTable->ui64TotalCost; + + if (RC_BAD( rc = setupTableScan( pSQLTable))) + { + goto Exit; + } + if (pSQLTable->pFSTableCursor->getCost() < ui64SaveCost) + { + pSQLTable->uiIndexNum = 0; + freeTableIndexes( pSQLTable); + } + else + { + + // Get rid of the table cursor that was set up and stay + // with the index cursors. + + pSQLTable->bScan = FALSE; + pSQLTable->pFSTableCursor->Release(); + pSQLTable->pFSTableCursor = NULL; + pSQLTable->ui64TotalCost = ui64SaveCost; + } } } } @@ -2443,58 +2581,253 @@ Exit: } //------------------------------------------------------------------------- -// Desc: Setup to scan an index - the index was specified by the user. +// Desc: Add an SQL_INDEX structure to the list of indexes for an +// SQL_TABLE structure. //------------------------------------------------------------------------- -RCODE SQLQuery::setupIndexScan( void) +RCODE SQLQuery::addIndexToTable( + SQL_TABLE * pSQLTable, + FLMUINT uiIndexNum, + SQL_INDEX ** ppSQLIndex) +{ + RCODE rc = NE_SFLM_OK; + SQL_INDEX * pSQLIndex; + + // Allocate the SQL_INDEX structure + + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_INDEX), + (void **)&pSQLIndex))) + { + goto Exit; + } + + // Link index to end of the list of indexes for the table. + + pSQLIndex->uiIndexNum = uiIndexNum; + if ((pSQLIndex->pPrev = pSQLTable->pLastSQLIndex) != NULL) + { + pSQLTable->pLastSQLIndex->pNext = pSQLIndex; + } + else + { + pSQLTable->pFirstSQLIndex = pSQLIndex; + } + pSQLTable->pLastSQLIndex = pSQLIndex; + + if (ppSQLIndex) + { + *ppSQLIndex = pSQLIndex; + } + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Add an SQL_KEY structure to the list of keys for an +// SQL_INDEX structure. +//------------------------------------------------------------------------- +RCODE SQLQuery::addKeyToIndex( + SQL_INDEX * pSQLIndex, + SQL_KEY ** ppSQLKey) +{ + RCODE rc = NE_SFLM_OK; + SQL_KEY * pSQLKey; + + // Allocate the SQL_KEY structure + + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( SQL_KEY), + (void **)&pSQLKey))) + { + goto Exit; + } + + // Link key to end of the list of keys for the index. + + if ((pSQLKey->pPrev = pSQLIndex->pLastSQLKey) != NULL) + { + pSQLIndex->pLastSQLKey->pNext = pSQLKey; + } + else + { + pSQLIndex->pFirstSQLKey = pSQLKey; + } + pSQLIndex->pLastSQLKey = pSQLKey; + + *ppSQLKey = pSQLKey; + +Exit: + + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Setup to scan an index for a table. +//------------------------------------------------------------------------- +RCODE SQLQuery::setupIndexScan( + SQL_TABLE * pSQLTable) { RCODE rc = NE_SFLM_OK; F_INDEX * pIndex; F_TABLE * pTable; - FLMBOOL bDoRowMatch; - FLMBOOL bCanCompareOnKey; FSIndexCursor * pFSIndexCursor = NULL; - - flmAssert( m_uiIndexNum); - - pIndex = m_pDb->m_pDict->getIndex( m_uiIndexNum); + SQL_INDEX * pSQLIndex; + SQL_KEY * pSQLKey; + + // At this point, there should not be any SQL_INDEX structures + // associated with the table as yet. -//VISIT: Make sure the index is on-line - - pTable = m_pDb->m_pDict->getTable( pIndex->uiTableNum); + flmAssert( !pSQLTable->pFirstSQLIndex); -//VISIT: Should only be one table in the table array, and it better match -//the table this index is associated with. -// if (pIndex->uiTableNum != m_uiTableNum) -// { -// rc = RC_SET( NE_SFLM_BAD_IX); -// goto Exit; -// } - - if ((pFSIndexCursor = f_new FSIndexCursor) == NULL) + if ((pIndex = m_pDb->m_pDict->getIndex( pSQLTable->uiIndexNum)) == NULL) { - rc = RC_SET( NE_SFLM_MEM); + rc = RC_SET( NE_SFLM_Q_INVALID_INDEX); + goto Exit; + } + if (pSQLTable->uiTableNum != pIndex->uiTableNum) + { + rc = RC_SET( NE_SFLM_Q_INVALID_TABLE_FOR_INDEX); + goto Exit; + } + pTable = m_pDb->m_pDict->getTable( pSQLTable->uiTableNum); + + // Make sure the index is not offline. + + if (pIndex->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) + { + rc = RC_SET( NE_SFLM_INDEX_OFFLINE); + goto Exit; } - // Setup to scan from beginning of key to end of key. - - if (RC_BAD( rc = pFSIndexCursor->setupKeys( m_pDb, pIndex, pTable, - NULL, 0, &bDoRowMatch, &bCanCompareOnKey, - NULL, NULL, NULL))) + // Allocate an index structure and associate it with the table. + + if (RC_BAD( rc = addIndexToTable( pSQLTable, pSQLTable->uiIndexNum, + &pSQLIndex))) { goto Exit; } - m_bScanIndex = TRUE; - m_bScan = FALSE; -//VISIT: Need to put the FSIndexCursor into the table structure - or wherever -// it belongs. + // Allocate a key structure and associate it with the index. + + if (RC_BAD( rc = addKeyToIndex( pSQLIndex, &pSQLKey))) + { + goto Exit; + } + + // Allocate an index cursor and set it up to scan the index from + // first to last. + + if ((pFSIndexCursor = f_new FSIndexCursor) == NULL) + { + rc = RC_SET( NE_SFLM_MEM); + goto Exit; + } + // Setup to scan from beginning of index to end of index. + + if (RC_BAD( rc = pFSIndexCursor->setupKeys( m_pDb, pIndex, pTable, NULL))) + { + goto Exit; + } + + // Setup a single key for the index. + + pSQLKey->pFSIndexCursor = pFSIndexCursor; + pSQLTable->ui64TotalCost = pFSIndexCursor->getCost(); + + // Set pFSIndexCursor to NULL so it will not be released + // below. + + pFSIndexCursor = NULL; + Exit: if (pFSIndexCursor) { pFSIndexCursor->Release(); } + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Setup to scan a table. +//------------------------------------------------------------------------- +RCODE SQLQuery::setupTableScan( + SQL_TABLE * pSQLTable) +{ + RCODE rc = NE_SFLM_OK; + FSTableCursor * pFSTableCursor = NULL; + + pSQLTable->bScan = TRUE; + + // No index set, or the index that was set was zero, so do + // a full table scan. + + if ((pFSTableCursor = f_new FSTableCursor) == NULL) + { + rc = RC_SET( NE_SFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pFSTableCursor->setupRange( m_pDb, + pSQLTable->uiTableNum, 1, FLM_MAX_UINT64, + TRUE))) + { + goto Exit; + } + pSQLTable->pFSTableCursor = pFSTableCursor; + pSQLTable->ui64TotalCost = pFSTableCursor->getCost(); + + // Set to NULL so it won't be released at Exit. + + pFSTableCursor = NULL; + +Exit: + + if (pFSTableCursor) + { + pFSTableCursor->Release(); + } + return( rc); +} + +//------------------------------------------------------------------------- +// Desc: Setup to scan each table that has been specified. It may be that +// indexes were specified for the tables - in which case we will +// setup to scan the indexes. +//------------------------------------------------------------------------- +RCODE SQLQuery::setupScans( void) +{ + RCODE rc = NE_SFLM_OK; + SQL_TABLE * pSQLTable; + + // If no tables were specified, the query will return an + // empty result. + + if (!m_pFirstSQLTable) + { + m_bEmpty = TRUE; + goto Exit; + } + + for (pSQLTable = m_pFirstSQLTable; pSQLTable; pSQLTable = pSQLTable->pNext) + { + if (pSQLTable->uiIndexNum) + { + if (RC_BAD( rc = setupIndexScan( pSQLTable))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = setupTableScan( pSQLTable))) + { + goto Exit; + } + } + } + +Exit: return( rc); } @@ -2541,18 +2874,13 @@ RCODE SQLQuery::optimize( void) m_uiLanguage = m_pDb->getDefaultLanguage(); - // An empty expression should scan the database and return everything. + // An empty expression should scan the tables listed - using either + // the index that was specified for the table, or just scanning + // the rows of the table. if (!m_pQuery) { - if (m_bIndexSet && m_uiIndexNum) - { - rc = setupIndexScan(); - } - else - { - m_bScan = TRUE; - } + rc = setupScans(); goto Exit; } @@ -2564,7 +2892,8 @@ RCODE SQLQuery::optimize( void) if (m_pQuery->currVal.eValType == SQL_BOOL_VAL && m_pQuery->currVal.val.eBool == SQL_TRUE) { - m_bScan = TRUE; + rc = setupScans(); + goto Exit; } else { @@ -2578,14 +2907,6 @@ RCODE SQLQuery::optimize( void) goto Exit; } - // If the user explicitly said to NOT use an index, we will not - - if (m_bIndexSet && !m_uiIndexNum) - { - m_bScan = TRUE; - goto Exit; - } - // Flatten the AND and OR operators in the query tree. Strip out // NOT operators, resolve constant arithmetic expressions, and // weed out boolean constants. diff --git a/sql/src/sqlquery.h b/sql/src/sqlquery.h index f2faf7e..bf4e9c8 100644 --- a/sql/src/sqlquery.h +++ b/sql/src/sqlquery.h @@ -211,20 +211,18 @@ FINLINE FLMBOOL sqlCanCompare( typedef struct SQL_KEY { - SQL_INDEX * pSQLIndex; + FSIndexCursor * pFSIndexCursor; + SQL_PRED ** ppPredicates; // Only used to set up pFSIndexCursor FLMUINT uiComponentsUsed; - SQL_PRED ** ppKeyComponents; - SQL_KEY * pNext; + SQL_KEY * pNext; // Keys are kept in ascending order. SQL_KEY * pPrev; } SQL_KEY; typedef struct SQL_INDEX { FLMUINT uiIndexNum; - FLMUINT uiNumComponents; - SQL_TABLE * pSQLTable; - SQL_KEY * pFirstKey; - SQL_KEY * pLastKey; + SQL_KEY * pFirstSQLKey; // First key in list - keys are kept in ascending order. + SQL_KEY * pLastSQLKey; // Last key in list SQL_INDEX * pNext; SQL_INDEX * pPrev; } SQL_INDEX; @@ -233,11 +231,9 @@ typedef struct SQL_TABLE { FLMUINT uiTableNum; FSTableCursor * pFSTableCursor; - FLMUINT64 ui64Cost; + FLMUINT64 ui64TotalCost; FLMBOOL bScan; - FLMBOOL bScanIndex; FLMUINT uiIndexNum; - FLMBOOL bIndexSet; SQL_INDEX * pFirstSQLIndex; SQL_INDEX * pLastSQLIndex; SQL_TABLE * pNext; @@ -422,6 +418,10 @@ public: FLMUINT uiTableNum, FLMUINT uiColumnNum, FLMBOOL bDescending); + + RCODE setIndex( + FLMUINT uiTableNum, + FLMUINT uiIndexNum); RCODE getNext( F_Row ** ppRow); @@ -469,21 +469,20 @@ private: RCODE convertToDNF( void); RCODE getPredKeys( - SQL_PRED * pPred, - SQL_TABLE * pSQLTable); + F_TABLE * pTable, + FLMUINT uiForceIndexNum, + SQL_PRED * pPred, + SQL_INDEX ** ppFirstSQLIndex, + SQL_INDEX ** ppLastSQLIndex); RCODE chooseBestIndex( - SQL_TABLE * pSQLTable, - FLMUINT64 * pui64Cost); - - RCODE calcTableScanCost( - SQL_TABLE * pSQLTable, - FLMUINT64 * pui64Cost, - FSTableCursor ** ppFSTableCursor); + F_TABLE * pTable, + SQL_INDEX * pFirstSQLIndex, + SQL_INDEX ** ppBestSQLIndex); RCODE mergeKeys( - SQL_TABLE * pDestTable, - SQL_TABLE * pSrcTable); + SQL_TABLE * pSQLTable, + SQL_INDEX * pSQLIndex); RCODE optimizeTable( SQL_SUBQUERY * pSubQuery, @@ -491,7 +490,22 @@ private: RCODE optimizeSubQueries( void); - RCODE setupIndexScan( void); + RCODE addIndexToTable( + SQL_TABLE * pSQLTable, + FLMUINT uiIndexNum, + SQL_INDEX ** ppSQLIndex); + + RCODE addKeyToIndex( + SQL_INDEX * pSQLIndex, + SQL_KEY ** ppSQLKey); + + RCODE setupIndexScan( + SQL_TABLE * pSQLTable); + + RCODE setupTableScan( + SQL_TABLE * pSQLTable); + + RCODE setupScans( void); RCODE optimize( void); @@ -512,10 +526,6 @@ private: FLMBOOL m_bOptimized; F_Database * m_pDatabase; F_Db * m_pDb; - FLMBOOL m_bScan; - FLMBOOL m_bScanIndex; - FLMUINT m_uiIndexNum; - FLMBOOL m_bIndexSet; FLMBOOL m_bEmpty; SQLQuery * m_pNext; SQLQuery * m_pPrev; @@ -551,4 +561,10 @@ RCODE sqlEvalCriteria( // sqleval.cpp F_Row * pRow, FLMUINT uiLanguage); -#endif // #ifndef FLAIMODBC_H +void freeIndexKeys( // whereclause.cpp + SQL_INDEX * pSQLIndex); + +void freeTableIndexes( // whereclause.cpp + SQL_TABLE * pSQLTable); + +#endif // #ifndef SQLQUERY_H diff --git a/sql/src/sqlstatement.cpp b/sql/src/sqlstatement.cpp index 52beef0..c46b6e9 100644 --- a/sql/src/sqlstatement.cpp +++ b/sql/src/sqlstatement.cpp @@ -1536,6 +1536,7 @@ Exit: //------------------------------------------------------------------------------ RCODE SQLStatement::getIndexName( FLMBOOL bMustExist, + F_TABLE * pTable, char * pszIndexName, FLMUINT uiIndexNameBufSize, FLMUINT * puiIndexNameLen, @@ -1585,6 +1586,19 @@ RCODE SQLStatement::getIndexName( rc = RC_SET( NE_SFLM_INVALID_SQL); goto Exit; } + else if (pTable) + { + if (pTable->uiTableNum != (*ppIndex)->uiTableNum) + { + setErrInfo( m_uiCurrLineNum, + uiTokenLineOffset, + SQL_ERR_INDEX_NOT_DEFINED_FOR_TABLE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_SFLM_INVALID_SQL); + goto Exit; + } + } } Exit: diff --git a/sql/src/sqlstatement.h b/sql/src/sqlstatement.h index 6a10da8..d983089 100644 --- a/sql/src/sqlstatement.h +++ b/sql/src/sqlstatement.h @@ -49,6 +49,8 @@ typedef struct TABLE_ITEM { FLMUINT uiTableNum; const char * pszTableAlias; + FLMUINT uiIndexNum; + FLMBOOL bScan; } TABLE_ITEM; typedef enum @@ -216,6 +218,7 @@ private: RCODE getIndexName( FLMBOOL bMustExist, + F_TABLE * pTable, char * pszIndexName, FLMUINT uiIndexNameBufSize, FLMUINT * puiIndexNameLen, diff --git a/sql/src/whereclause.cpp b/sql/src/whereclause.cpp index 06b399b..fc6677e 100644 --- a/sql/src/whereclause.cpp +++ b/sql/src/whereclause.cpp @@ -82,11 +82,7 @@ SQLQuery::SQLQuery() m_pLastOrderBy = NULL; m_bResolveNames = FALSE; m_bOptimized = FALSE; - m_bScan = FALSE; - m_bScanIndex = FALSE; m_bEmpty = FALSE; - m_uiIndexNum = 0; - m_bIndexSet = FALSE; m_pQuery = NULL; m_pDatabase = NULL; m_pDb = NULL; @@ -94,12 +90,67 @@ SQLQuery::SQLQuery() m_pPrev = NULL; } +//------------------------------------------------------------------------- +// Desc: freel all of the SQL_KEY structures associated with an SQL_INDEX. +//------------------------------------------------------------------------- +void freeIndexKeys( + SQL_INDEX * pSQLIndex) +{ + SQL_KEY * pSQLKey; + + for (pSQLKey = pSQLIndex->pFirstSQLKey; pSQLKey; pSQLKey = pSQLKey->pNext) + { + if (pSQLKey->pFSIndexCursor) + { + pSQLKey->pFSIndexCursor->Release(); + pSQLKey->pFSIndexCursor = NULL; + } + } + pSQLIndex->pFirstSQLKey = NULL; + pSQLIndex->pLastSQLKey = NULL; +} + +//------------------------------------------------------------------------- +// Desc: Free all of the SQL_INDEX structures associated with an SQL_TABLE. +//------------------------------------------------------------------------- +void freeTableIndexes( + SQL_TABLE * pSQLTable) +{ + SQL_INDEX * pSQLIndex; + + for (pSQLIndex = pSQLTable->pFirstSQLIndex; pSQLIndex; pSQLIndex = pSQLIndex->pNext) + { + freeIndexKeys( pSQLIndex); + } + pSQLTable->pFirstSQLIndex = NULL; + pSQLTable->pLastSQLIndex = NULL; +} + //------------------------------------------------------------------------- // Desc: Destructor //------------------------------------------------------------------------- SQLQuery::~SQLQuery() { + SQL_TABLE * pSQLTable; + + // Free all of the table and index cursors. + + for (pSQLTable = m_pFirstSQLTable; pSQLTable; pSQLTable = pSQLTable->pNext) + { + if (pSQLTable->pFSTableCursor) + { + pSQLTable->pFSTableCursor->Release(); + pSQLTable->pFSTableCursor = NULL; + } + freeTableIndexes( pSQLTable); + } + + // Free all of the memory allocated from the memory pool. + m_pool.poolFree(); + + // Unlink the query from the database it is associated with. + if (m_pDatabase) { m_pDatabase->lockMutex(); @@ -512,6 +563,12 @@ RCODE SQLQuery::addTable( } m_pLastSQLTable = pSQLTable; } + + // These should have been set by the poolCalloc. + + // pSQLTable->bScan = FALSE; + // pSQLTable->uiIndexNum = 0; + if (ppSQLTable) { *ppSQLTable = pSQLTable; @@ -1147,6 +1204,45 @@ Exit: return( rc); } +//------------------------------------------------------------------------- +// Desc: Set an index on a table. +//------------------------------------------------------------------------- +RCODE SQLQuery::setIndex( + FLMUINT uiTableNum, + FLMUINT uiIndexNum) +{ + RCODE rc = NE_SFLM_OK; + SQL_TABLE * pSQLTable; + + flmAssert( !m_bOptimized); + + // Find the table structure for the table - should already exist. + + pSQLTable = m_pFirstSQLTable; + while (pSQLTable && pSQLTable->uiTableNum != uiTableNum) + { + pSQLTable = pSQLTable->pNext; + } + if (!pSQLTable) + { + rc = RC_BAD( NE_SFLM_Q_INVALID_TABLE_FOR_INDEX); + goto Exit; + } + + if ((pSQLTable->uiIndexNum = uiIndexNum) == 0) + { + pSQLTable->bScan = TRUE; + } + else + { + pSQLTable->bScan = FALSE; + } + +Exit: + + return( rc); +} + //------------------------------------------------------------------------------ // Desc: Determine if the current token is one that would terminate the // expression.