Example #1
0
        private List <RowIdentifier> ParseClonedRows(Dictionary <Int16, ExecutionPlan> exucutionPlanByServer)
        {
            var clonedRows = new List <RowIdentifier>();

            foreach (var server in exucutionPlanByServer)
            {
                foreach (var row in server.Value.InsertSteps.Where(s => s.Depth == 0))
                {
                    var pkTemp = row.TableSchema.BuildPkFromDataRow(row.Datarow);

                    //Clone for new reference
                    var clonedPk = new ColumnsWithValue();
                    foreach (var col in pkTemp)
                    {
                        var sqlVar = col.Value as SqlVariable;
                        clonedPk.Add(col.Key, sqlVar != null ? sqlVar.Value : col.Value);
                    }

                    var riReturn = new RowIdentifier
                    {
                        ServerId = row.DestinationTable.ServerId,
                        Database = row.DestinationTable.Database,
                        Schema   = row.DestinationTable.Schema,
                        Table    = row.DestinationTable.Table,
                        Columns  = clonedPk
                    };

                    //Construit la pk de retour
                    clonedRows.Add(riReturn);
                }
            }

            return(clonedRows);
        }
        private object[] GetDataRow(RowIdentifier riNewFk)
        {
            if (riNewFk == null)
            {
                throw new ArgumentNullException(nameof(riNewFk));
            }
            if (!riNewFk.Columns.Any())
            {
                throw new Exception("Failed to return a foreign key value.");
            }

            //La FK (ou unique constraint) n'est pas necessairement la PK donc on réobtient la ligne car
            //BuildExecutionPlan retourne toujours la PK.
            var sqlVar = riNewFk.Columns.Values.First() as SqlVariable;

            if (sqlVar == null)
            {
                throw new Exception();
            }

            var varId = sqlVar.Id;

            foreach (var plan in _executionPlanByServer)
            {
                var dr = plan.Value.InsertSteps.FirstOrDefault(r => r.Variables.Any(v => v.Id == varId));
                if (dr != null)
                {
                    return(dr.Datarow);
                }
            }
            throw new Exception();
        }
Example #3
0
 public StatusChangedEventArgs(Status status, int currentIndex, int maxIndex, RowIdentifier sourceRow, int level)
 {
     Status       = status;
     CurrentIndex = currentIndex;
     MaxIndex     = maxIndex;
     SourceRow    = sourceRow;
     Level        = level;
 }
        private void LogStatusChanged(RowIdentifier riSource, int level)
        {
            var clientCopy = riSource.Clone();

            if (StatusChanged != null)
            {
                var args = new StatusChangedEventArgs(Status.Cloning, 0, 0, clientCopy, level);
                StatusChanged(this, args);
            }
        }
        public ExecutionPlanBuilder Append(RowIdentifier riSource, bool getDerivatives = true)
        {
            if (riSource == null)
            {
                throw new ArgumentNullException(nameof(riSource));
            }

            _steps.Add(riSource);
            var rowsGenerating = new Stack <RowIdentifier>();

            rowsGenerating.Push(riSource);

            BuildExecutionPlan(riSource, getDerivatives, false, 0, rowsGenerating);
            BuildCircularReferencesPlan();

            return(this);
        }
        private void GetDerivatives(TableMetadata sourceTable, object[] sourceRow, bool getDerivatives, int level,
                                    Stack <RowIdentifier> rowsGenerating)
        {
            LogDerivativeStep(level + 1);
            var derivativeTable = getDerivatives ? sourceTable.DerivativeTables : sourceTable.DerivativeTables.Where(t => t.Access == DerivativeTableAccess.Forced);

            foreach (var dt in derivativeTable)
            {
                var tableDt = MetadataContainer.Metadatas.GetTable(dt);
                if (dt.Access == DerivativeTableAccess.Forced && dt.Cascade)
                {
                    getDerivatives = true;
                }
                else if (dt.Access == DerivativeTableAccess.Denied)
                {
                    continue;
                }

                var riDt = new RowIdentifier
                {
                    ServerId = dt.ServerId,
                    Database = dt.Database,
                    Schema   = dt.Schema,
                    Table    = dt.Table,
                    Columns  = sourceTable.BuildDerivativePk(tableDt, sourceRow)
                };

                var rows = _dispatcher.Select(riDt);

                //Pour chaque ligne dérivée de la table source
                foreach (var row in rows)
                {
                    riDt.Columns = tableDt.BuildPkFromDataRow(row);

                    rowsGenerating.Push(riDt);
                    BuildExecutionPlan(riDt, getDerivatives, false, level + 1, rowsGenerating);
                    rowsGenerating.Pop();
                }
            }
        }
        private void BuildCircularReferencesPlan()
        {
            foreach (var job in _circularKeyJobs)
            {
                var baseTable           = MetadataContainer.Metadatas.GetTable(job.SourceBaseRowStartPoint);
                var fkTable             = MetadataContainer.Metadatas.GetTable(job.SourceFkRowStartPoint);
                var pkDestinationRow    = _keyRelationships.GetKey(job.SourceBaseRowStartPoint);
                var keyDestinationFkRow = _keyRelationships.GetKey(job.SourceFkRowStartPoint);

                var serverDstBaseTable = MetadataContainer.ServerMap[new ServerIdentifier
                                                                     {
                                                                         ServerId = job.SourceBaseRowStartPoint.ServerId,
                                                                         Database = job.SourceBaseRowStartPoint.Database,
                                                                         Schema = job.SourceBaseRowStartPoint.Schema
                                                                     }];

                var serverDstFkTable = MetadataContainer.ServerMap[new ServerIdentifier
                                                                   {
                                                                       ServerId = job.SourceFkRowStartPoint.ServerId,
                                                                       Database = job.SourceFkRowStartPoint.Database,
                                                                       Schema = job.SourceFkRowStartPoint.Schema
                                                                   }];

                if (job.ForeignKey.Columns.Count != keyDestinationFkRow.Length)
                {
                    throw new Exception("The foreign key defenition is not matching with the values.");
                }

                var fk = new ColumnsWithValue();
                for (var i = 0; i < job.ForeignKey.Columns.Count; i++)
                {
                    var colName = job.ForeignKey.Columns[i].NameTo;
                    var value   = keyDestinationFkRow[i];

                    fk.Add(colName, value);
                }

                var riBaseDestination = new RowIdentifier
                {
                    ServerId = serverDstBaseTable.ServerId,
                    Database = serverDstBaseTable.Database,
                    Schema   = serverDstBaseTable.Schema,
                    Table    = job.SourceBaseRowStartPoint.Table,
                    Columns  = baseTable.BuildPkFromRawKey(pkDestinationRow)
                };

                var riFkDestination = new RowIdentifier
                {
                    ServerId = serverDstFkTable.ServerId,
                    Database = serverDstFkTable.Database,
                    Schema   = serverDstFkTable.Schema,
                    Table    = job.SourceFkRowStartPoint.Table,
                    Columns  = fk
                };

                var fkDestinationDataRow = GetDataRow(riFkDestination);
                var modifiedFk           = fkTable.BuildKeyFromFkDataRow(job.ForeignKey, fkDestinationDataRow);

                var step = new UpdateStep()
                {
                    StepId           = _nextStepId++,
                    DestinationRow   = riBaseDestination,
                    ForeignKey       = modifiedFk,
                    DestinationTable = riFkDestination
                };

                AddUpdateStep(step);
            }
            _circularKeyJobs.Clear();
        }
        /// <summary>
        /// Build execution plan for the specific source row to be able to clone blazing fast.
        /// </summary>
        /// <param name="riSource">Identify a single or multiples plan to clone.</param>
        /// <param name="getDerivatives">Specify if we clone data related to the input(s) line(s) from other tables.</param>
        /// <param name="shouldReturnFk">Indicate that a source row should only return a single line.</param>
        /// <param name="level">Current recursion level.</param>
        /// <param name="rowsGenerating">Current stack to handle circular foreign keys.</param>
        /// <returns>Always return the primary key of the source row, same if the value queried is a foreign key.</returns>
        private RowIdentifier BuildExecutionPlan(RowIdentifier riSource, bool getDerivatives, bool shouldReturnFk, int level,
                                                 Stack <RowIdentifier> rowsGenerating)
        {
            var srcRows = _dispatcher.Select(riSource);
            var nbRows  = srcRows.Length;
            var table   = MetadataContainer.Metadatas.GetTable(riSource);

            //By default the destination server is the source if no road is found.
            var serverDst = new ServerIdentifier
            {
                ServerId = riSource.ServerId,
                Database = riSource.Database,
                Schema   = riSource.Schema
            };

            if (MetadataContainer.ServerMap.ContainsKey(serverDst))
            {
                serverDst = MetadataContainer.ServerMap[serverDst];
            }

            var riReturn = new RowIdentifier
            {
                ServerId = serverDst.ServerId,
                Database = serverDst.Database,
                Schema   = serverDst.Schema,
                Table    = riSource.Table
            };
            var tiDestination = new TableIdentifier
            {
                ServerId = serverDst.ServerId,
                Database = serverDst.Database,
                Schema   = serverDst.Schema,
                Table    = riSource.Table
            };

            LogStatusChanged(riSource, level);

            if (shouldReturnFk && nbRows > 1)
            {
                throw new Exception("The foreignkey is not unique!");
            }

            //For each row
            for (var i = 0; i < nbRows; i++)
            {
                var currentRow = srcRows[i];
                var srcKey     = table.BuildRawPkFromDataRow(currentRow);

                //Si ligne déjà enregistrée
                var dstKey = _keyRelationships.GetKey(serverDst.ServerId, serverDst.Database,
                                                      serverDst.Schema, riSource.Table, srcKey);
                if (dstKey != null)
                {
                    if (shouldReturnFk)
                    {
                        //Construit la pk de retour
                        riReturn.Columns = table.BuildPkFromRawKey(dstKey);
                        return(riReturn);
                    }
                    continue;
                }
                var destinationRow = (object[])currentRow.Clone();
                foreach (var fk in table.ForeignKeys)
                {
                    //On skip si la FK est null
                    var fkValue = table.BuildRawFkFromDataRow(fk, currentRow);
                    if (fkValue.Contains(DBNull.Value))
                    {
                        continue;
                    }

                    //Si la foreignkey a déjà été enregistrée, on l'utilise
                    var fkDst = _keyRelationships.GetKey(fk.ServerIdTo, fk.DatabaseTo, fk.SchemaTo, fk.TableTo, fkValue);
                    if (fkDst != null)
                    {
                        table.SetFkInDatarow(fk, fkDst, destinationRow);
                    }
                    else
                    {
                        var fkDestinationExists = false;
                        var fkTable             = MetadataContainer.Metadatas.GetTable(fk);
                        var riFk = new RowIdentifier
                        {
                            ServerId = fk.ServerIdTo,
                            Database = fk.DatabaseTo,
                            Schema   = fk.SchemaTo,
                            Table    = fk.TableTo,
                            Columns  = table.BuildKeyFromDerivativeDataRow(fk, currentRow)
                        };

                        //On ne duplique pas la ligne si la table est statique
                        if (fkTable.IsStatic)
                        {
                            //TODO : Tester si la FK existe dans la table de destination de clônage et non si la fk existe dans la bd source
                            var fkRow = _dispatcher.Select(riFk);
                            fkDestinationExists = fkRow.Length == 1;

                            if (fkRow.Length > 1)
                            {
                                throw new Exception("The FK is not unique.");
                            }

                            //Si la ligne existe déjà, on l'utilise
                            if (fkDestinationExists)
                            {
                                //Sauve la clef
                                var colFkObj = fkTable.BuildRawPkFromDataRow(fkRow[0]);
                                _keyRelationships.SetKey(fk.ServerIdTo, fk.DatabaseTo, fk.SchemaTo, fk.TableTo, colFkObj, colFkObj);
                            }
                        }

                        //La FK n'existe pas, on la crer
                        if (!fkDestinationExists)
                        {
                            //Si référence circulaire
                            if (rowsGenerating.Contains(riFk))
                            {
                                //Affecte la FK à 1 pour les contraintes NOT NULL. EnforceIntegrity doit être désactivé.
                                var nullFk = Enumerable.Repeat <object>(1, fk.Columns.Count).ToArray();
                                table.SetFkInDatarow(fk, nullFk, destinationRow);

                                //On ajoute une tâche pour réassigner la FK "correctement", une fois que toute la chaîne aura été enregistrée.
                                _circularKeyJobs.Add(new CircularKeyJob
                                {
                                    SourceBaseRowStartPoint = new RowIdentifier
                                    {
                                        ServerId = riSource.ServerId,
                                        Database = riSource.Database,
                                        Schema   = riSource.Schema,
                                        Table    = riSource.Table,
                                        Columns  = table.BuildPkFromDataRow(currentRow)
                                    },
                                    SourceFkRowStartPoint = riFk,
                                    ForeignKey            = fk
                                });
                            }
                            else
                            {
                                //Crer la ligne et ses dépendances
                                rowsGenerating.Push(riFk);
                                var riNewFk = BuildExecutionPlan(riFk, false, true, level + 1, rowsGenerating);
                                rowsGenerating.Pop();

                                var newFkRow = GetDataRow(riNewFk);

                                //Affecte la clef
                                table.SetFkFromDatarowInDatarow(fkTable, fk, newFkRow, destinationRow);
                            }
                        }
                    }
                }

                var step = CreateExecutionStep(riSource, tiDestination, table, destinationRow, level);

                //Sauve la PK dans la cache
                dstKey = table.BuildRawPkFromDataRow(step.Datarow);
                _keyRelationships.SetKey(riSource.ServerId, riSource.Database, riSource.Schema, riSource.Table, srcKey, dstKey);

                //Ajouter les colonnes de contrainte unique dans _keyRelationships
                //...

                //On affecte la valeur de retour
                if (shouldReturnFk)
                {
                    riReturn.Columns = table.BuildPkFromRawKey(dstKey);
                }

                //On clone les lignes des tables dépendantes
                GetDerivatives(table, currentRow, getDerivatives, level, rowsGenerating);
            }

            return(riReturn);
        }