Ejemplo n.º 1
0
        /// <summary>
        /// This function does all the nasty work at determining what keys need to be returned for
        /// a given statement.
        /// </summary>
        /// <param name="cnn"></param>
        /// <param name="reader"></param>
        /// <param name="stmt"></param>
        internal SQLiteKeyReader(SQLiteConnection cnn, SQLiteDataReader reader, SQLiteStatement stmt)
        {
            Dictionary <string, int>            catalogs = new Dictionary <string, int>();
            Dictionary <string, List <string> > tables   = new Dictionary <string, List <string> >();
            List <string>    list;
            List <KeyInfo>   keys   = new List <KeyInfo>();
            List <RowIdInfo> rowIds = new List <RowIdInfo>();

            // Record the statement so we can use it later for sync'ing
            _stmt = stmt;

            // Fetch all the attached databases on this connection
            using (DataTable tbl = cnn.GetSchema("Catalogs"))
            {
                foreach (DataRow row in tbl.Rows)
                {
                    catalogs.Add((string)row["CATALOG_NAME"], Convert.ToInt32(row["ID"], CultureInfo.InvariantCulture));
                }
            }

            // Fetch all the unique tables and catalogs used by the current statement
            using (DataTable schema = reader.GetSchemaTable(false, false))
            {
                foreach (DataRow row in schema.Rows)
                {
                    // Check if column is backed to a table
                    if (row[SchemaTableOptionalColumn.BaseCatalogName] == DBNull.Value)
                    {
                        continue;
                    }

                    // Record the unique table so we can look up its keys
                    string catalog = (string)row[SchemaTableOptionalColumn.BaseCatalogName];
                    string table   = (string)row[SchemaTableColumn.BaseTableName];

                    if (tables.ContainsKey(catalog) == false)
                    {
                        list = new List <string>();
                        tables.Add(catalog, list);
                    }
                    else
                    {
                        list = tables[catalog];
                    }

                    if (list.Contains(table) == false)
                    {
                        list.Add(table);
                    }
                }

                // For each catalog and each table, query the indexes for the table.
                // Find a primary key index if there is one.  If not, find a unique index instead
                foreach (KeyValuePair <string, List <string> > pair in tables)
                {
                    for (int i = 0; i < pair.Value.Count; i++)
                    {
                        string  table        = pair.Value[i];
                        DataRow preferredRow = null;
                        using (DataTable tbl = cnn.GetSchema("Indexes", new string[] { pair.Key, null, table }))
                        {
                            // Loop twice.  The first time looking for a primary key index,
                            // the second time looking for a unique index
                            for (int n = 0; n < 2 && preferredRow == null; n++)
                            {
                                foreach (DataRow row in tbl.Rows)
                                {
                                    if (n == 0 && (bool)row["PRIMARY_KEY"] == true)
                                    {
                                        preferredRow = row;
                                        break;
                                    }
                                    else if (n == 1 && (bool)row["UNIQUE"] == true)
                                    {
                                        preferredRow = row;
                                        break;
                                    }
                                }
                            }
                            if (preferredRow == null) // Unable to find any suitable index for this table so remove it
                            {
                                pair.Value.RemoveAt(i);
                                i--;
                            }
                            else // We found a usable index, so fetch the necessary table details
                            {
                                using (DataTable tblTables = cnn.GetSchema("Tables", new string[] { pair.Key, null, table }))
                                {
                                    // Find the root page of the table in the current statement and get the cursor that's iterating it
                                    int database = catalogs[pair.Key];
                                    int rootPage = Convert.ToInt32(tblTables.Rows[0]["TABLE_ROOTPAGE"], CultureInfo.InvariantCulture);
                                    int cursor   = stmt._sql.GetCursorForTable(stmt, database, rootPage);

                                    // Now enumerate the members of the index we're going to use
                                    using (DataTable indexColumns = cnn.GetSchema("IndexColumns", new string[] { pair.Key, null, table, (string)preferredRow["INDEX_NAME"] }))
                                    {
                                        //
                                        // NOTE: If this is actually a RowId (or alias), record that now.  There should
                                        //       be exactly one index column in that case.
                                        //
                                        bool     isRowId = (string)preferredRow["INDEX_NAME"] == "sqlite_master_PK_" + table;
                                        KeyQuery query   = null;

                                        List <string> cols = new List <string>();
                                        for (int x = 0; x < indexColumns.Rows.Count; x++)
                                        {
                                            string columnName = SQLiteConvert.GetStringOrNull(
                                                indexColumns.Rows[x]["COLUMN_NAME"]);

                                            bool addKey = true;
                                            // If the column in the index already appears in the query, skip it
                                            foreach (DataRow row in schema.Rows)
                                            {
                                                if (row.IsNull(SchemaTableColumn.BaseColumnName))
                                                {
                                                    continue;
                                                }

                                                if ((string)row[SchemaTableColumn.BaseColumnName] == columnName &&
                                                    (string)row[SchemaTableColumn.BaseTableName] == table &&
                                                    (string)row[SchemaTableOptionalColumn.BaseCatalogName] == pair.Key)
                                                {
                                                    if (isRowId)
                                                    {
                                                        RowIdInfo rowId = new RowIdInfo();

                                                        rowId.databaseName = pair.Key;
                                                        rowId.tableName    = table;
                                                        rowId.column       = (int)row[SchemaTableColumn.ColumnOrdinal];

                                                        rowIds.Add(rowId);
                                                    }
                                                    indexColumns.Rows.RemoveAt(x);
                                                    x--;
                                                    addKey = false;
                                                    break;
                                                }
                                            }
                                            if (addKey == true)
                                            {
                                                cols.Add(columnName);
                                            }
                                        }

                                        // If the index is not a rowid alias, record all the columns
                                        // needed to make up the unique index and construct a SQL query for it
                                        if (!isRowId)
                                        {
                                            // Whatever remains of the columns we need that make up the index that are not
                                            // already in the query need to be queried separately, so construct a subquery
                                            if (cols.Count > 0)
                                            {
                                                string[] querycols = new string[cols.Count];
                                                cols.CopyTo(querycols);
                                                query = new KeyQuery(cnn, pair.Key, table, querycols);
                                            }
                                        }

                                        // Create a KeyInfo struct for each column of the index
                                        for (int x = 0; x < indexColumns.Rows.Count; x++)
                                        {
                                            string  columnName = SQLiteConvert.GetStringOrNull(indexColumns.Rows[x]["COLUMN_NAME"]);
                                            KeyInfo key        = new KeyInfo();

                                            key.rootPage     = rootPage;
                                            key.cursor       = cursor;
                                            key.database     = database;
                                            key.databaseName = pair.Key;
                                            key.tableName    = table;
                                            key.columnName   = columnName;
                                            key.query        = query;
                                            key.column       = x;

                                            keys.Add(key);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // Now we have all the additional columns we have to return in order to support
            // CommandBehavior.KeyInfo
            _keyInfo = new KeyInfo[keys.Count];
            keys.CopyTo(_keyInfo);

            _rowIdInfo = new RowIdInfo[rowIds.Count];
            rowIds.CopyTo(_rowIdInfo);
        }