// Uses SQLStatistics to retrieve key information for a table private int RetrieveKeyInfoFromStatistics(QualifiedTableName qualifiedTableName, bool quoted) { ODBC32.RetCode retcode; String columnname = String.Empty; String indexname = String.Empty; String currentindexname = String.Empty; int[] indexcolumnordinals = new int[16]; int[] pkcolumnordinals = new int[16]; int npkcols = 0; int ncols = 0; // No of cols in the index bool partialcolumnset = false; int ordinal; int indexordinal; IntPtr cbIndexLen = IntPtr.Zero; IntPtr cbColnameLen = IntPtr.Zero; int keyColumns = 0; // devnote: this test is already done by calling method ... // if (IsClosed) return; // protect against dead connection // MDAC String tablename1 = String.Copy(qualifiedTableName.GetTable(quoted)); // Select only unique indexes retcode = KeyInfoStatementHandle.Statistics(tablename1); if (retcode != ODBC32.RetCode.SUCCESS) { // We give up at this point return 0; } CNativeBuffer buffer = Buffer; bool mustRelease = false; Debug.Assert(buffer.Length >= 544, "Native buffer to small (_buffer.Length < 544)"); RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.DangerousAddRef(ref mustRelease); const int colnameBufOffset = 0; const int indexBufOffset = 256; const int ordinalBufOffset = 512; const int colnameActualOffset = 520; const int indexActualOffset = 528; const int ordinalActualOffset = 536; HandleRef colnamebuf = buffer.PtrOffset(colnameBufOffset, 256); HandleRef indexbuf = buffer.PtrOffset(indexBufOffset, 256); HandleRef ordinalbuf = buffer.PtrOffset(ordinalBufOffset, 4); IntPtr colnameActual = buffer.PtrOffset(colnameActualOffset, IntPtr.Size).Handle; IntPtr indexActual = buffer.PtrOffset(indexActualOffset, IntPtr.Size).Handle; IntPtr ordinalActual = buffer.PtrOffset(ordinalActualOffset, IntPtr.Size).Handle; //We are interested in index name, column name, and ordinal buffer.WriteInt16(indexBufOffset, 0); retcode = KeyInfoStatementHandle.BindColumn2( (short)(ODBC32.SQL_STATISTICS.INDEXNAME), ODBC32.SQL_C.WCHAR, indexbuf, (IntPtr)256, indexActual); retcode = KeyInfoStatementHandle.BindColumn2( (short)(ODBC32.SQL_STATISTICS.ORDINAL_POSITION), ODBC32.SQL_C.SSHORT, ordinalbuf, (IntPtr)4, ordinalActual); buffer.WriteInt16(ordinalBufOffset, 0); retcode = KeyInfoStatementHandle.BindColumn2( (short)(ODBC32.SQL_STATISTICS.COLUMN_NAME), ODBC32.SQL_C.WCHAR, colnamebuf, (IntPtr)256, colnameActual); // Find the best unique index on the table, use the ones whose columns are // completely covered by the query. while (ODBC32.RetCode.SUCCESS == (retcode = KeyInfoStatementHandle.Fetch())) { cbColnameLen = buffer.ReadIntPtr(colnameActualOffset); cbIndexLen = buffer.ReadIntPtr(indexActualOffset); // If indexname is not returned, skip this row if (0 == buffer.ReadInt16(indexBufOffset)) continue; // Not an index row, get next row. columnname = buffer.PtrToStringUni(colnameBufOffset, (int)cbColnameLen/2/*cch*/); indexname = buffer.PtrToStringUni(indexBufOffset, (int)cbIndexLen/2/*cch*/); ordinal = (int) buffer.ReadInt16(ordinalBufOffset); if (SameIndexColumn(currentindexname, indexname, ordinal, ncols)) { // We are still working on the same index if (partialcolumnset) continue; // We don't have all the keys for this index, so we can't use it ordinal = this.GetOrdinalFromBaseColName(columnname, qualifiedTableName.Table); if (ordinal == -1) { partialcolumnset = true; } else { // Add the column to the index column set if (ncols < 16) indexcolumnordinals[ncols++] = ordinal; else // Can't deal with indexes > 16 columns partialcolumnset = true; } } else { // We got a new index, save the previous index information if (!partialcolumnset && (ncols != 0)) { // Choose the unique index with least columns as primary key if ((npkcols == 0) || (npkcols > ncols)){ npkcols = ncols; for (int i = 0 ; i < ncols ; i++) pkcolumnordinals[i] = indexcolumnordinals[i]; } } // Reset the parameters for a new index ncols = 0; currentindexname = indexname; partialcolumnset = false; // Add this column to index ordinal = this.GetOrdinalFromBaseColName(columnname, qualifiedTableName.Table); if (ordinal == -1) { partialcolumnset = true; } else { // Add the column to the index column set indexcolumnordinals[ncols++] = ordinal; } } // Go on to the next column } // Do we have an index? if (!partialcolumnset && (ncols != 0)) { // Choose the unique index with least columns as primary key if ((npkcols == 0) || (npkcols > ncols)){ npkcols = ncols; for (int i = 0 ; i < ncols ; i++) pkcolumnordinals[i] = indexcolumnordinals[i]; } } // Mark the chosen index as primary key if (npkcols != 0) { for (int i = 0 ; i < npkcols ; i++) { indexordinal = pkcolumnordinals[i]; keyColumns++; this.metadata[indexordinal].isKeyColumn = true; // should we set isNullable = false? // This makes the QuikTest against Jet fail // // test test test - we don't know if this is nulalble or not so why do we want to set it to a value? this.metadata[indexordinal].isNullable = false; this.metadata[indexordinal].isUnique = true; if (this.metadata[indexordinal].baseTableName == null) { this.metadata[indexordinal].baseTableName = qualifiedTableName.Table; } if (this.metadata[indexordinal].baseColumnName == null) { this.metadata[indexordinal].baseColumnName = columnname; } } } // Unbind the columns _cmdWrapper.FreeKeyInfoStatementHandle(ODBC32.STMT.UNBIND); } finally { if (mustRelease) { buffer.DangerousRelease(); } } return keyColumns; }
private int RetrieveKeyInfoFromStatistics(QualifiedTableName qualifiedTableName, bool quoted) { string columnname = string.Empty; string indexname = string.Empty; string currentindexname = string.Empty; int[] numArray = new int[0x10]; int[] numArray2 = new int[0x10]; int num2 = 0; int ncols = 0; bool flag = false; IntPtr zero = IntPtr.Zero; IntPtr ptr = IntPtr.Zero; int num8 = 0; string tableName = string.Copy(qualifiedTableName.GetTable(quoted)); if (this.KeyInfoStatementHandle.Statistics(tableName) != ODBC32.RetCode.SUCCESS) { return 0; } CNativeBuffer buffer = this.Buffer; bool success = false; RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.DangerousAddRef(ref success); HandleRef ref4 = buffer.PtrOffset(0, 0x100); HandleRef ref3 = buffer.PtrOffset(0x100, 0x100); HandleRef ref2 = buffer.PtrOffset(0x200, 4); IntPtr handle = buffer.PtrOffset(520, IntPtr.Size).Handle; IntPtr ptr4 = buffer.PtrOffset(0x210, IntPtr.Size).Handle; IntPtr ptr3 = buffer.PtrOffset(0x218, IntPtr.Size).Handle; buffer.WriteInt16(0x100, 0); ODBC32.RetCode code = this.KeyInfoStatementHandle.BindColumn2(6, ODBC32.SQL_C.WCHAR, ref3, (IntPtr) 0x100, ptr4); code = this.KeyInfoStatementHandle.BindColumn2(8, ODBC32.SQL_C.SSHORT, ref2, (IntPtr) 4, ptr3); buffer.WriteInt16(0x200, 0); code = this.KeyInfoStatementHandle.BindColumn2(9, ODBC32.SQL_C.WCHAR, ref4, (IntPtr) 0x100, handle); while ((code = this.KeyInfoStatementHandle.Fetch()) == ODBC32.RetCode.SUCCESS) { ptr = buffer.ReadIntPtr(520); zero = buffer.ReadIntPtr(0x210); if (buffer.ReadInt16(0x100) != 0) { columnname = buffer.PtrToStringUni(0, ((int) ptr) / 2); indexname = buffer.PtrToStringUni(0x100, ((int) zero) / 2); int ordinal = buffer.ReadInt16(0x200); if (this.SameIndexColumn(currentindexname, indexname, ordinal, ncols)) { if (!flag) { ordinal = this.GetOrdinalFromBaseColName(columnname, qualifiedTableName.Table); if (ordinal == -1) { flag = true; continue; } if (ncols < 0x10) { numArray[ncols++] = ordinal; } else { flag = true; } } } else { if ((!flag && (ncols != 0)) && ((num2 == 0) || (num2 > ncols))) { num2 = ncols; for (int i = 0; i < ncols; i++) { numArray2[i] = numArray[i]; } } ncols = 0; currentindexname = indexname; flag = false; ordinal = this.GetOrdinalFromBaseColName(columnname, qualifiedTableName.Table); if (ordinal == -1) { flag = true; } else { numArray[ncols++] = ordinal; } } } } if ((!flag && (ncols != 0)) && ((num2 == 0) || (num2 > ncols))) { num2 = ncols; for (int j = 0; j < ncols; j++) { numArray2[j] = numArray[j]; } } if (num2 != 0) { for (int k = 0; k < num2; k++) { int index = numArray2[k]; num8++; this.metadata[index].isKeyColumn = true; this.metadata[index].isNullable = false; this.metadata[index].isUnique = true; if (this.metadata[index].baseTableName == null) { this.metadata[index].baseTableName = qualifiedTableName.Table; } if (this.metadata[index].baseColumnName == null) { this.metadata[index].baseColumnName = columnname; } } } this._cmdWrapper.FreeKeyInfoStatementHandle(ODBC32.STMT.UNBIND); } finally { if (success) { buffer.DangerousRelease(); } } return num8; }
internal int RetrieveKeyInfo(bool needkeyinfo, QualifiedTableName qualifiedTableName, bool quoted) { ODBC32.RetCode retcode; string columnname; int ordinal; int keyColumns = 0; IntPtr cbActual = IntPtr.Zero; if (IsClosed || (_cmdWrapper == null)) { return 0; // Can't do anything without a second handle } _cmdWrapper.CreateKeyInfoStatementHandle(); CNativeBuffer buffer = Buffer; bool mustRelease = false; Debug.Assert(buffer.Length >= 264, "Native buffer to small (_buffer.Length < 264)"); RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.DangerousAddRef(ref mustRelease); if (needkeyinfo) { if (!Connection.ProviderInfo.NoSqlPrimaryKeys) { // Get the primary keys retcode = KeyInfoStatementHandle.PrimaryKeys( qualifiedTableName.Catalog, qualifiedTableName.Schema, qualifiedTableName.GetTable(quoted)); if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)) { bool noUniqueKey = false; // We are only interested in column name buffer.WriteInt16(0, 0); retcode = KeyInfoStatementHandle.BindColumn2( (short)(ODBC32.SQL_PRIMARYKEYS.COLUMNNAME), // Column Number ODBC32.SQL_C.WCHAR, buffer.PtrOffset(0, 256), (IntPtr)256, buffer.PtrOffset(256, IntPtr.Size).Handle); while (ODBC32.RetCode.SUCCESS == (retcode = KeyInfoStatementHandle.Fetch())) { cbActual = buffer.ReadIntPtr(256); columnname = buffer.PtrToStringUni(0, (int)cbActual/2/*cch*/); ordinal = this.GetOrdinalFromBaseColName(columnname); if (ordinal != -1) { keyColumns ++; this.metadata[ordinal].isKeyColumn = true; this.metadata[ordinal].isUnique = true; this.metadata[ordinal].isNullable = false; this.metadata[ordinal].baseTableName = qualifiedTableName.Table; if (this.metadata[ordinal].baseColumnName == null) { this.metadata[ordinal].baseColumnName = columnname; } } else { noUniqueKey = true; break; // no need to go over the remaining columns anymore } } // // if we got keyinfo from the column we dont even get to here! // // reset isUnique flag if the key(s) are not unique // if (noUniqueKey) { foreach (MetaData metadata in this.metadata) { metadata.isKeyColumn = false; } } // Unbind the column retcode = KeyInfoStatementHandle.BindColumn3( (short)(ODBC32.SQL_PRIMARYKEYS.COLUMNNAME), // SQLUSMALLINT ColumnNumber ODBC32.SQL_C.WCHAR, // SQLSMALLINT TargetType buffer.DangerousGetHandle()); // SQLLEN * StrLen_or_Ind } else { if ("IM001" == Command.GetDiagSqlState()) { Connection.ProviderInfo.NoSqlPrimaryKeys = true; } } } if (keyColumns == 0) { // SQLPrimaryKeys did not work. Have to use the slower SQLStatistics to obtain key information KeyInfoStatementHandle.MoreResults(); keyColumns += RetrieveKeyInfoFromStatistics(qualifiedTableName, quoted); } KeyInfoStatementHandle.MoreResults(); } // Get the special columns for version retcode = KeyInfoStatementHandle.SpecialColumns(qualifiedTableName.GetTable(quoted)); if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)) { // We are only interested in column name cbActual = IntPtr.Zero; buffer.WriteInt16(0, 0); retcode = KeyInfoStatementHandle.BindColumn2( (short)(ODBC32.SQL_SPECIALCOLUMNSET.COLUMN_NAME), ODBC32.SQL_C.WCHAR, buffer.PtrOffset(0, 256), (IntPtr)256, buffer.PtrOffset(256, IntPtr.Size).Handle); while (ODBC32.RetCode.SUCCESS == (retcode = KeyInfoStatementHandle.Fetch())) { cbActual = buffer.ReadIntPtr(256); columnname = buffer.PtrToStringUni(0, (int)cbActual/2/*cch*/); ordinal = this.GetOrdinalFromBaseColName(columnname); if (ordinal != -1) { this.metadata[ordinal].isRowVersion = true; if (this.metadata[ordinal].baseColumnName == null) { this.metadata[ordinal].baseColumnName = columnname; } } } // Unbind the column retcode = KeyInfoStatementHandle.BindColumn3( (short)(ODBC32.SQL_SPECIALCOLUMNSET.COLUMN_NAME), ODBC32.SQL_C.WCHAR, buffer.DangerousGetHandle()); retcode = KeyInfoStatementHandle.MoreResults(); } else { // i've seen "DIAG [HY000] [Microsoft][ODBC SQL Server Driver]Connection is busy with results for another hstmt (0) " // how did we get here? SqlServer does not allow a second handle (Keyinfostmt) anyway... // /* string msg = "Unexpected failure of SQLSpecialColumns. Code = " + Command.GetDiagSqlState(); Debug.Assert (false, msg); */ } } finally { if (mustRelease) { buffer.DangerousRelease(); } } return keyColumns; }
internal int RetrieveKeyInfo(bool needkeyinfo, QualifiedTableName qualifiedTableName, bool quoted) { int num2 = 0; IntPtr zero = IntPtr.Zero; if (this.IsClosed || (this._cmdWrapper == null)) { return 0; } this._cmdWrapper.CreateKeyInfoStatementHandle(); CNativeBuffer buffer = this.Buffer; bool success = false; RuntimeHelpers.PrepareConstrainedRegions(); try { int ordinalFromBaseColName; ODBC32.RetCode code; string str; buffer.DangerousAddRef(ref success); if (!needkeyinfo) { goto Label_0206; } if (!this.Connection.ProviderInfo.NoSqlPrimaryKeys) { code = this.KeyInfoStatementHandle.PrimaryKeys(qualifiedTableName.Catalog, qualifiedTableName.Schema, qualifiedTableName.GetTable(quoted)); switch (code) { case ODBC32.RetCode.SUCCESS: case ODBC32.RetCode.SUCCESS_WITH_INFO: { bool flag = false; buffer.WriteInt16(0, 0); code = this.KeyInfoStatementHandle.BindColumn2(4, ODBC32.SQL_C.WCHAR, buffer.PtrOffset(0, 0x100), (IntPtr) 0x100, buffer.PtrOffset(0x100, IntPtr.Size).Handle); while ((code = this.KeyInfoStatementHandle.Fetch()) == ODBC32.RetCode.SUCCESS) { zero = buffer.ReadIntPtr(0x100); str = buffer.PtrToStringUni(0, ((int) zero) / 2); ordinalFromBaseColName = this.GetOrdinalFromBaseColName(str); if (ordinalFromBaseColName != -1) { num2++; this.metadata[ordinalFromBaseColName].isKeyColumn = true; this.metadata[ordinalFromBaseColName].isUnique = true; this.metadata[ordinalFromBaseColName].isNullable = false; this.metadata[ordinalFromBaseColName].baseTableName = qualifiedTableName.Table; if (this.metadata[ordinalFromBaseColName].baseColumnName == null) { this.metadata[ordinalFromBaseColName].baseColumnName = str; } } else { flag = true; break; } } if (flag) { foreach (MetaData data in this.metadata) { data.isKeyColumn = false; } } code = this.KeyInfoStatementHandle.BindColumn3(4, ODBC32.SQL_C.WCHAR, buffer.DangerousGetHandle()); goto Label_01E0; } } if ("IM001" == this.Command.GetDiagSqlState()) { this.Connection.ProviderInfo.NoSqlPrimaryKeys = true; } } Label_01E0: if (num2 == 0) { this.KeyInfoStatementHandle.MoreResults(); num2 += this.RetrieveKeyInfoFromStatistics(qualifiedTableName, quoted); } this.KeyInfoStatementHandle.MoreResults(); Label_0206: code = this.KeyInfoStatementHandle.SpecialColumns(qualifiedTableName.GetTable(quoted)); if ((code != ODBC32.RetCode.SUCCESS) && (code != ODBC32.RetCode.SUCCESS_WITH_INFO)) { return num2; } zero = IntPtr.Zero; buffer.WriteInt16(0, 0); code = this.KeyInfoStatementHandle.BindColumn2(2, ODBC32.SQL_C.WCHAR, buffer.PtrOffset(0, 0x100), (IntPtr) 0x100, buffer.PtrOffset(0x100, IntPtr.Size).Handle); while ((code = this.KeyInfoStatementHandle.Fetch()) == ODBC32.RetCode.SUCCESS) { zero = buffer.ReadIntPtr(0x100); str = buffer.PtrToStringUni(0, ((int) zero) / 2); ordinalFromBaseColName = this.GetOrdinalFromBaseColName(str); if (ordinalFromBaseColName != -1) { this.metadata[ordinalFromBaseColName].isRowVersion = true; if (this.metadata[ordinalFromBaseColName].baseColumnName == null) { this.metadata[ordinalFromBaseColName].baseColumnName = str; } } } code = this.KeyInfoStatementHandle.BindColumn3(2, ODBC32.SQL_C.WCHAR, buffer.DangerousGetHandle()); code = this.KeyInfoStatementHandle.MoreResults(); } finally { if (success) { buffer.DangerousRelease(); } } return num2; }