protected override void PopulateTable(bool fresh)
        {
            using (var connection = GetConnection())
            {
                connection.Open();

                SpannerCommand command        = connection.CreateInsertOrUpdateCommand(TableName);
                var            keyParameter   = command.Parameters.Add("K", SpannerDbType.String, IdGenerator.FromGuid());
                var            valueParameter = command.Parameters.Add("StringValue", SpannerDbType.String, IdGenerator.FromGuid());

                // Create a lowest-bound timestamp which is definitely valid, but not
                // associated with TestKey
                RetryHelpers.RetryOnce(() =>
                {
                    using (var tx = connection.BeginTransaction())
                    {
                        command.Transaction = tx;
                        command.ExecuteNonQuery();
                        tx.Commit(out var timestamp);
                        TimestampBeforeEntries = timestamp.Value;
                    }
                });

                // It's a pity we have to do this, but otherwise it's hard to get reliable tests.
                Thread.Sleep(Staleness + TimeSpan.FromSeconds(2));
            }
        }
 public void CommandTypeSupportsOnlyCommandText()
 {
     var command = new SpannerCommand();
     Assert.Throws<NotSupportedException>(() => command.CommandType = CommandType.StoredProcedure);
     Assert.Throws<NotSupportedException>(() => command.CommandType = CommandType.TableDirect);
     command.CommandType = CommandType.Text;
     Assert.Equal(CommandType.Text, command.CommandType);
 }
예제 #3
0
    public async Task <int> WriteWithTransactionUsingDmlCoreAsync(string projectId, string instanceId, string databaseId)
    {
        // This sample transfers 200,000 from the MarketingBudget
        // field of the second Album to the first Album. Make sure to run
        // the AddColumnAsyncSample and WriteDataToNewColumnAsyncSample first,
        // in that order.
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        decimal transferAmount = 200000;
        decimal secondBudget   = 0;

        // Create connection to Cloud Spanner.
        using var connection = new SpannerConnection(connectionString);
        await connection.OpenAsync();

        // Create a readwrite transaction that we'll assign
        // to each SpannerCommand.
        using var transaction = await connection.BeginTransactionAsync();

        // Create statement to select the second album's data.
        var cmdLookup = connection.CreateSelectCommand("SELECT * FROM Albums WHERE SingerId = 2 AND AlbumId = 2");

        cmdLookup.Transaction = transaction;
        // Execute the select query.
        using var reader1 = await cmdLookup.ExecuteReaderAsync();

        while (await reader1.ReadAsync())
        {
            // Read the second album's budget.
            secondBudget = reader1.GetFieldValue <decimal>("MarketingBudget");
            // Confirm second Album's budget is sufficient and
            // if not raise an exception. Raising an exception
            // will automatically roll back the transaction.
            if (secondBudget < transferAmount)
            {
                throw new Exception($"The second album's budget {secondBudget} is less than the amount to transfer.");
            }
        }

        // Update second album to remove the transfer amount.
        secondBudget -= transferAmount;
        SpannerCommand cmd = connection.CreateDmlCommand("UPDATE Albums SET MarketingBudget = @MarketingBudget  WHERE SingerId = 2 and AlbumId = 2");

        cmd.Parameters.Add("MarketingBudget", SpannerDbType.Int64, secondBudget);
        cmd.Transaction = transaction;
        var rowCount = await cmd.ExecuteNonQueryAsync();

        // Update first album to add the transfer amount.
        cmd = connection.CreateDmlCommand("UPDATE Albums SET MarketingBudget = MarketingBudget + @MarketingBudgetIncrement WHERE SingerId = 1 and AlbumId = 1");
        cmd.Parameters.Add("MarketingBudgetIncrement", SpannerDbType.Int64, transferAmount);
        cmd.Transaction = transaction;
        rowCount       += await cmd.ExecuteNonQueryAsync();

        await transaction.CommitAsync();

        Console.WriteLine("Transaction complete.");
        return(rowCount);
    }
 internal SpannerDataReaderWithChecksum(
     SpannerRetriableTransaction transaction,
     SpannerDataReader spannerDataReader,
     SpannerCommand command)
 {
     Transaction        = transaction;
     _spannerDataReader = spannerDataReader;
     _spannerCommand    = (SpannerCommand)command.Clone();
 }
 public void UpdateRowSourceNotSupported()
 {
     var command = new SpannerCommand();
     Assert.Throws<NotSupportedException>(() => command.UpdatedRowSource = UpdateRowSource.Both);
     Assert.Throws<NotSupportedException>(() => command.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord);
     Assert.Throws<NotSupportedException>(() => command.UpdatedRowSource = UpdateRowSource.OutputParameters);
     command.UpdatedRowSource = UpdateRowSource.None;
     Assert.Equal(UpdateRowSource.None, command.UpdatedRowSource);
 }
        internal async Task <object> ExecuteScalarWithRetryAsync(SpannerCommand command, CancellationToken cancellationToken)
        {
            using var reader = await ExecuteDbDataReaderWithRetryAsync(command, cancellationToken);

            if (await reader.ReadAsync(cancellationToken))
            {
                return(reader.GetValue(0));
            }

            return(null);
        }
        public async Task SingleUseSelect()
        {
            string connectionString = $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port}";

            // Create connection to Cloud Spanner.
            using var connection = new SpannerConnection(connectionString, ChannelCredentials.Insecure);
            SpannerCommand cmd = connection.CreateSelectCommand("SELECT 1");

            using var reader = await cmd.ExecuteReaderAsync();

            while (await reader.ReadAsync())
            {
                Assert.Equal(1, reader.GetInt64(0));
            }
        }
예제 #8
0
        private Task InsertRowAsync(SpannerCommand writeCommand)
        {
            string k = UniqueString();

            _addedKeys.Add(k);
            writeCommand.Parameters["K"].Value = k;
            _stringValues[k] = (string)(writeCommand.Parameters["StringValue"].Value =
                                            GetLargeString(MinDataSize, MaxDataSize));
            _stringArrayValues[k] = (string[])(writeCommand.Parameters["StringArrayValue"].Value =
                                                   GetLargeStringArray());
            _bytesValues[k] = (byte[])(writeCommand.Parameters["BytesValue"].Value =
                                           GetLargeBytes(MinDataSize, MaxDataSize));
            _bytesArrayValues[k] = (byte[][])(writeCommand.Parameters["BytesArrayValue"].Value = GetLargeBytesArray());

            return(writeCommand.ExecuteNonQueryAsync());
        }
예제 #9
0
        public static async Task InsertPlayersAsync(string projectId,
                                                    string instanceId, string databaseId)
        {
            string connectionString =
                $"Data Source=projects/{projectId}/instances/{instanceId}"
                + $"/databases/{databaseId}";

            long numberOfPlayers = 0;

            using (var connection = new SpannerConnection(connectionString))
            {
                await connection.OpenAsync();

                await connection.RunWithRetriableTransactionAsync(async (transaction) =>
                {
                    // Execute a SQL statement to get current number of records
                    // in the Players table to use as an incrementing value
                    // for each PlayerName to be inserted.
                    var cmd = connection.CreateSelectCommand(
                        @"SELECT Count(PlayerId) as PlayerCount FROM Players");
                    numberOfPlayers = await cmd.ExecuteScalarAsync <long>();
                    // Insert 100 player records into the Players table.
                    SpannerBatchCommand cmdBatch = connection.CreateBatchDmlCommand();
                    for (int i = 0; i < 100; i++)
                    {
                        numberOfPlayers++;
                        SpannerCommand cmdInsert = connection.CreateDmlCommand(
                            "INSERT INTO Players "
                            + "(PlayerId, PlayerName) "
                            + "VALUES (@PlayerId, @PlayerName)",
                            new SpannerParameterCollection {
                            { "PlayerId", SpannerDbType.Int64 },
                            { "PlayerName", SpannerDbType.String }
                        });
                        cmdInsert.Parameters["PlayerId"].Value =
                            Math.Abs(Guid.NewGuid().GetHashCode());
                        cmdInsert.Parameters["PlayerName"].Value =
                            $"Player {numberOfPlayers}";
                        cmdBatch.Add(cmdInsert);
                    }
                    await cmdBatch.ExecuteNonQueryAsync();
                });
            }
            Console.WriteLine("Done inserting player records...");
        }
예제 #10
0
    public async Task <int> WriteUsingDmlCoreAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        await connection.OpenAsync();

        SpannerCommand cmd = connection.CreateDmlCommand(
            "INSERT Singers (SingerId, FirstName, LastName) VALUES "
            + "(12, 'Melissa', 'Garcia'), "
            + "(13, 'Russell', 'Morales'), "
            + "(14, 'Jacqueline', 'Long'), "
            + "(15, 'Dylan', 'Shaw')");
        int rowCount = await cmd.ExecuteNonQueryAsync();

        Console.WriteLine($"{rowCount} row(s) inserted...");
        return(rowCount);
    }
        public async Task WriteMutations()
        {
            string connectionString = $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port}";

            using (var connection = new SpannerConnection(connectionString, ChannelCredentials.Insecure))
            {
                SpannerCommand cmd = connection.CreateInsertOrUpdateCommand("Singers", new SpannerParameterCollection {
                    { "SingerId", SpannerDbType.Int64, 1 },
                    { "FirstName", SpannerDbType.String, "FirstName1" },
                    { "LastName", SpannerDbType.String, "LastName1" },
                });
                await cmd.ExecuteNonQueryAsync();
            }
            IEnumerable <IMessage> requests = _fixture.SpannerMock.Requests;
            CommitRequest          commit   = (CommitRequest)requests.Last();

            Assert.Equal(Mutation.OperationOneofCase.InsertOrUpdate, commit.Mutations.First().OperationCase);
            Assert.Equal("Singers", commit.Mutations.First().InsertOrUpdate.Table);
        }
예제 #12
0
    public async Task <int> UpdateDataAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);

        var            rowCount = 0;
        SpannerCommand cmd      = connection.CreateDmlCommand(
            "UPDATE Albums SET MarketingBudget = @MarketingBudget "
            + "WHERE SingerId = 1 and AlbumId = 1");

        cmd.Parameters.Add("MarketingBudget", SpannerDbType.Int64, 100000);
        rowCount += await cmd.ExecuteNonQueryAsync();

        cmd = connection.CreateDmlCommand(
            "UPDATE Albums SET MarketingBudget = @MarketingBudget "
            + "WHERE SingerId = 2 and AlbumId = 2");
        cmd.Parameters.Add("MarketingBudget", SpannerDbType.Int64, 500000);
        rowCount += await cmd.ExecuteNonQueryAsync();

        Console.WriteLine("Data Updated.");
        return(rowCount);
    }
        protected internal override async Task <int> ExecuteNonQueryWithRetryAsync(SpannerCommand command, CancellationToken cancellationToken = default)
        {
            while (true)
            {
                command.Transaction = SpannerTransaction;
                try
                {
                    int res = await command.ExecuteNonQueryAsync(cancellationToken);

                    _retriableStatements.Add(new RetriableDmlStatement(command, res));
                    return(res);
                }
                catch (SpannerException e) when(e.ErrorCode == ErrorCode.Aborted)
                {
                    await RetryAsync(e, cancellationToken);
                }
                catch (SpannerException e)
                {
                    _retriableStatements.Add(new FailedDmlStatement(command, e));
                    throw;
                }
            }
        }
예제 #14
0
 /// <inheritdoc/>
 protected internal override DbDataReader ExecuteDbDataReaderWithRetry(SpannerCommand command)
 {
     GaxPreconditions.CheckState(!Disposed, "This transaction has been disposed");
     command.Transaction = SpannerTransaction;
     return(command.ExecuteReader());
 }
예제 #15
0
 public void Add(SpannerCommand command) => _commands.Add(command);
예제 #16
0
 /// <summary>
 /// Executes a <see cref="SpannerCommand"/> as a query and returns the first column of the
 /// first row. The entire transaction is retried if the command fails with an Aborted error.
 /// </summary>
 /// <param name="command">The command to execute. Must be a query.</param>
 /// <returns>The value of the first column of the first row in the query result</returns>
 protected internal abstract object ExecuteScalarWithRetry(SpannerCommand command);
예제 #17
0
 /// <summary>
 /// Executes a <see cref="SpannerCommand"/> as an update statement and retries the entire
 /// transaction if the command fails with an Aborted error.
 /// </summary>
 /// <param name="command">The command to execute. Must be a DML or mutation command.</param>
 /// <param name="cancellationToken"></param>
 /// <returns>The number of affected rows.</returns>
 protected internal abstract Task <int> ExecuteNonQueryWithRetryAsync(SpannerCommand command, CancellationToken cancellationToken);
예제 #18
0
 /// <summary>
 /// Executes a <see cref="SpannerCommand"/> as an update statement and retries the entire
 /// transaction if the command fails with an Aborted error.
 /// </summary>
 /// <param name="command">The command to execute. Must be a DML or mutation command.</param>
 /// <returns>The number of affected rows.</returns>
 protected internal abstract int ExecuteNonQueryWithRetry(SpannerCommand command);
예제 #19
0
 /// <summary>
 /// Read-only transactions cannot execute non-query statements. Calling this method will throw an <see cref="InvalidOperationException"/>.
 /// </summary>
 /// <exception cref="InvalidOperationException"></exception>
 protected internal override Task <int> ExecuteNonQueryWithRetryAsync(SpannerCommand command, CancellationToken cancellationToken) =>
 throw new InvalidOperationException("Non-query operations are not allowed on a read-only transaction");
예제 #20
0
        public void CreatesSpannerParameter()
        {
            var command = new SpannerCommand();

            Assert.IsType <SpannerParameter>(command.CreateParameter());
        }
예제 #21
0
        public static async Task InsertScoresAsync(
            string projectId, string instanceId, string databaseId)
        {
            string connectionString =
                $"Data Source=projects/{projectId}/instances/{instanceId}"
                + $"/databases/{databaseId}";

            // Insert 4 score records into the Scores table for each player
            // in the Players table.
            using (var connection = new SpannerConnection(connectionString))
            {
                await connection.OpenAsync();

                await connection.RunWithRetriableTransactionAsync(async (transaction) =>
                {
                    Random r = new Random();
                    bool playerRecordsFound      = false;
                    SpannerBatchCommand cmdBatch =
                        connection.CreateBatchDmlCommand();
                    var cmdLookup =
                        connection.CreateSelectCommand("SELECT * FROM Players");
                    using (var reader = await cmdLookup.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            playerRecordsFound = true;
                            for (int i = 0; i < 4; i++)
                            {
                                DateTime randomTimestamp = DateTime.Now
                                                           .AddYears(r.Next(-2, 1))
                                                           .AddMonths(r.Next(-12, 1))
                                                           .AddDays(r.Next(-10, 1))
                                                           .AddSeconds(r.Next(-60, 0))
                                                           .AddMilliseconds(r.Next(-100000, 0));
                                SpannerCommand cmdInsert =
                                    connection.CreateDmlCommand(
                                        "INSERT INTO Scores "
                                        + "(PlayerId, Score, Timestamp) "
                                        + "VALUES (@PlayerId, @Score, @Timestamp)",
                                        new SpannerParameterCollection {
                                    { "PlayerId", SpannerDbType.Int64 },
                                    { "Score", SpannerDbType.Int64 },
                                    { "Timestamp",
                                      SpannerDbType.Timestamp }
                                });
                                cmdInsert.Parameters["PlayerId"].Value =
                                    reader.GetFieldValue <int>("PlayerId");
                                cmdInsert.Parameters["Score"].Value =
                                    r.Next(1000, 1000001);
                                cmdInsert.Parameters["Timestamp"].Value =
                                    randomTimestamp.ToString("o");
                                cmdBatch.Add(cmdInsert);
                            }
                        }
                        if (!playerRecordsFound)
                        {
                            Console.WriteLine("Parameter 'scores' is invalid "
                                              + "since no player records currently exist. First "
                                              + "insert players then insert scores.");
                            Environment.Exit((int)ExitCode.InvalidParameter);
                        }
                        else
                        {
                            await cmdBatch.ExecuteNonQueryAsync();
                            Console.WriteLine(
                                "Done inserting score records..."
                                );
                        }
                    }
                });
            }
        }
 internal SpannerRetriableCommand(SpannerRetriableConnection connection, SpannerCommand spannerCommand)
 {
     _connection     = connection;
     _spannerCommand = (SpannerCommand)GaxPreconditions.CheckNotNull(spannerCommand, nameof(spannerCommand)).Clone();
 }
        internal async Task <SpannerDataReaderWithChecksum> ExecuteDbDataReaderWithRetryAsync(SpannerCommand command, CancellationToken cancellationToken)
        {
            // This method does not need a retry loop as it is not actually executing the query. Instead,
            // that will be deferred until the first call to DbDataReader.Read().
            command.Transaction = SpannerTransaction;
            var spannerReader = await command.ExecuteReaderAsync(cancellationToken);

            var checksumReader = new SpannerDataReaderWithChecksum(this, spannerReader, command);

            _retriableStatements.Add(checksumReader);
            return(checksumReader);
        }
 protected internal override DbDataReader ExecuteDbDataReaderWithRetry(SpannerCommand command)
 => Task.Run(() => ExecuteDbDataReaderWithRetryAsync(command, CancellationToken.None)).ResultWithUnwrappedExceptions();
예제 #25
0
 internal RetriableDmlStatement(SpannerCommand command, long updateCount)
 {
     _command     = (SpannerCommand)command.Clone();
     _updateCount = updateCount;
 }
예제 #26
0
        public static async Task InsertPlayersAsync(string projectId,
                                                    string instanceId, string databaseId)
        {
            string connectionString =
                $"Data Source=projects/{projectId}/instances/{instanceId}"
                + $"/databases/{databaseId}";

            using (TransactionScope scope = new TransactionScope(
                       TransactionScopeAsyncFlowOption.Enabled))
            {
                Int64 numberOfPlayers = 0;
                using (var connection = new SpannerConnection(connectionString))
                {
                    await connection.OpenAsync();

                    // Execute a SQL statement to get current number of records
                    // in the Players table to use as an incrementing value
                    // for each PlayerName to be inserted.
                    var cmd = connection.CreateSelectCommand(
                        @"SELECT Count(PlayerId) as PlayerCount FROM Players");
                    using (var reader = await cmd.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            long parsedValue;
                            if (reader["PlayerCount"] != DBNull.Value)
                            {
                                bool result = Int64.TryParse(
                                    reader.GetFieldValue <string>("PlayerCount"),
                                    out parsedValue);
                                if (result)
                                {
                                    numberOfPlayers = parsedValue;
                                }
                            }
                        }
                    }
                    // Insert 100 player records into the Players table.
                    SpannerBatchCommand cmdBatch = connection.CreateBatchDmlCommand();
                    for (var x = 1; x <= 100; x++)
                    {
                        numberOfPlayers++;
                        SpannerCommand cmdInsert = connection.CreateDmlCommand(
                            "INSERT INTO Players "
                            + "(PlayerId, PlayerName) "
                            + "VALUES (@PlayerId, @PlayerName)",
                            new SpannerParameterCollection {
                            { "PlayerId", SpannerDbType.Int64 },
                            { "PlayerName", SpannerDbType.String }
                        });
                        cmdInsert.Parameters["PlayerId"].Value =
                            Math.Abs(Guid.NewGuid().GetHashCode());
                        cmdInsert.Parameters["PlayerName"].Value =
                            $"Player {numberOfPlayers}";
                        cmdBatch.Add(cmdInsert);
                    }
                    await cmdBatch.ExecuteNonQueryAsync();

                    scope.Complete();
                }
            }
            Console.WriteLine("Done inserting player records...");
        }
예제 #27
0
        public void PrepareNoop()
        {
            var command = new SpannerCommand();

            command.Prepare();
        }
예제 #28
0
 /// <summary>
 /// Executes a <see cref="SpannerCommand"/> as a query and returns the result as a
 /// <see cref="DbDataReader"/> that will retry the entire transaction if the query or any
 /// of the results of the underlying stream of PartialResultSets returns an Aborted error.
 /// </summary>
 /// <param name="command">The command to execute. Must be a query.</param>
 /// <returns>
 /// The results of the query as a <see cref="DbDataReader"/> that will automatically retry
 /// the entire transaction if the result stream returns an Aborted error.
 /// </returns>
 protected internal abstract DbDataReader ExecuteDbDataReaderWithRetry(SpannerCommand command);
 /// <summary>
 /// Executes a single command, retrying once if the first attempt is aborted.
 /// Only for use in "one-shot" commands (not in a transaction).
 public static Task <int> ExecuteNonQueryAsyncWithRetry(this SpannerCommand command) =>
 ExecuteWithRetryAsyncImpl(() => command.ExecuteNonQueryAsync());
예제 #30
0
 /// <summary>
 /// Read-only transactions cannot execute non-query statements. Calling this method will throw an <see cref="InvalidOperationException"/>.
 /// </summary>
 /// <exception cref="InvalidOperationException"></exception>
 protected internal override int ExecuteNonQueryWithRetry(SpannerCommand command) =>
 throw new InvalidOperationException("Non-query operations are not allowed on a read-only transaction");