blob: 96b31543eb2edb8c24ad63b00c7ee4cdf840139d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
namespace Apache.IoTDB.Data
{
/// <summary>
/// Represents a connection to a IoTDB database.
/// </summary>
public partial class IoTDBConnection : DbConnection
{
private readonly IList<WeakReference<IoTDBCommand>> _commands = new List<WeakReference<IoTDBCommand>>();
private string _connectionString;
private ConnectionState _state;
internal SessionPool _IoTDB;
/// <summary>
/// Initializes a new instance of the <see cref="IoTDBConnection" /> class.
/// </summary>
public IoTDBConnection()
{
_IoTDB = (ConnectionStringBuilder ?? new IoTDBConnectionStringBuilder()).CreateSession();
}
public SessionPool SessionPool => _IoTDB;
/// <summary>
/// Initializes a new instance of the <see cref="IoTDBConnection" /> class.
/// </summary>
/// <param name="connectionString">The string used to open the connection.</param>
/// <seealso cref="IoTDBConnectionStringBuilder" />
public IoTDBConnection(string connectionString) : this()
{
ConnectionStringBuilder = new IoTDBConnectionStringBuilder(connectionString);
ConnectionString = connectionString;
}
/// <summary>
/// Gets or sets a string used to open the connection.
/// </summary>
/// <value>A string used to open the connection.</value>
/// <seealso cref="IoTDBConnectionStringBuilder" />
public override string ConnectionString
{
get => _connectionString;
set
{
_connectionString = value;
ConnectionStringBuilder = new IoTDBConnectionStringBuilder(value);
_IoTDB = ConnectionStringBuilder.CreateSession();
}
}
internal IoTDBConnectionStringBuilder ConnectionStringBuilder { get; set; }
/// <summary>
/// Gets the path to the database file. Will be absolute for open connections.
/// </summary>
/// <value>The path to the database file.</value>
public override string DataSource
{
get
{
string dataSource = null;
return dataSource ?? ConnectionStringBuilder.DataSource;
}
}
/// <summary>
/// Gets or sets the default <see cref="IoTDBCommand.CommandTimeout"/> value for commands created using
/// this connection. This is also used for internal commands in methods like
/// <see cref="BeginTransaction()"/>.
/// </summary>
/// <value>The default <see cref="IoTDBCommand.CommandTimeout"/> value</value>
public virtual int DefaultTimeout { get; set; } = 60;
/// <summary>
/// Gets the version of IoTDB used by the connection.
/// </summary>
/// <value>The version of IoTDB used by the connection.</value>
public override string ServerVersion
{
get
{
throw new NotImplementedException();
}
}
public string ClientVersion
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
/// Gets the current state of the connection.
/// </summary>
/// <value>The current state of the connection.</value>
public override ConnectionState State
=> _state;
/// <summary>
/// Gets the <see cref="DbProviderFactory" /> for this connection.
/// </summary>
/// <value>The <see cref="DbProviderFactory" />.</value>
protected override DbProviderFactory DbProviderFactory
=> IoTDBFactory.Instance;
/// <summary>
/// Gets or sets the transaction currently being used by the connection, or null if none.
/// </summary>
/// <value>The transaction currently being used by the connection.</value>
protected internal virtual IoTDBTransaction Transaction { get; set; }
private void SetState(ConnectionState value)
{
var originalState = _state;
if (originalState != value)
{
_state = value;
OnStateChange(new StateChangeEventArgs(originalState, value));
}
}
/// <summary>
/// Opens a connection to the database using the value of <see cref="ConnectionString" />. If
/// <c>Mode=ReadWriteCreate</c> is used (the default) the file is created, if it doesn't already exist.
/// </summary>
/// <exception cref="IoTDBException">A IoTDB error occurs while opening the connection.</exception>
public override void Open()
{
OpenAsync().GetAwaiter().GetResult();
}
public override async Task OpenAsync(CancellationToken cancellationToken = default)
{
if (State == ConnectionState.Open)
{
return;
}
if (ConnectionString == null)
{
throw new InvalidOperationException("Open Requires Set ConnectionString");
}
await _IoTDB.Open(ConnectionStringBuilder.Compression, cancellationToken);
if (!_IoTDB.IsOpen())
{
IoTDBException.ThrowExceptionForRC(-1, "Can't open IoTDB server.");
}
else
{
SetState(ConnectionState.Open);
}
}
/// <summary>
/// Closes the connection to the database. Open transactions are rolled back.
/// </summary>
public override void Close()
{
CloseAsync().GetAwaiter().GetResult(); ;
}
#if NET461_OR_GREATER || NETSTANDARD2_0
public async Task CloseAsync()
#else
public override async Task CloseAsync()
#endif
{
if (State != ConnectionState.Closed)
await _IoTDB.Close();
Transaction?.Dispose();
_nowdatabase = string.Empty;
foreach (var reference in _commands)
{
if (reference.TryGetTarget(out var command))
{
command.Dispose();
}
}
_commands.Clear();
SetState(ConnectionState.Closed);
}
/// <summary>
/// Releases any resources used by the connection and closes it.
/// </summary>
/// <param name="disposing">
/// true to release managed and unmanaged resources; false to release only unmanaged resources.
/// </param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
Close();
}
base.Dispose(disposing);
}
/// <summary>
/// Creates a new command associated with the connection.
/// </summary>
/// <returns>The new command.</returns>
/// <remarks>
/// The command's <seealso cref="IoTDBCommand.Transaction" /> property will also be set to the current
/// transaction.
/// </remarks>
public new virtual IoTDBCommand CreateCommand()
=> new IoTDBCommand { Connection = this, CommandTimeout = DefaultTimeout, Transaction = Transaction };
public virtual IoTDBCommand CreateCommand(string commandtext)
=> new IoTDBCommand { Connection = this, CommandText = commandtext, CommandTimeout = DefaultTimeout, Transaction = Transaction };
/// <summary>
/// Creates a new command associated with the connection.
/// </summary>
/// <returns>The new command.</returns>
protected override DbCommand CreateDbCommand()
=> CreateCommand();
internal void AddCommand(IoTDBCommand command)
=> _commands.Add(new WeakReference<IoTDBCommand>(command));
internal void RemoveCommand(IoTDBCommand command)
{
for (var i = _commands.Count - 1; i >= 0; i--)
{
if (!_commands[i].TryGetTarget(out var item)
|| item == command)
{
_commands.RemoveAt(i);
}
}
}
/// <summary>
/// Create custom collation.
/// </summary>
/// <param name="name">Name of the collation.</param>
/// <param name="comparison">Method that compares two strings.</param>
public virtual void CreateCollation(string name, Comparison<string> comparison)
=> CreateCollation(name, null, comparison != null ? (_, s1, s2) => comparison(s1, s2) : (Func<object, string, string, int>)null);
/// <summary>
/// Create custom collation.
/// </summary>
/// <typeparam name="T">The type of the state object.</typeparam>
/// <param name="name">Name of the collation.</param>
/// <param name="state">State object passed to each invocation of the collation.</param>
/// <param name="comparison">Method that compares two strings, using additional state.</param>
public virtual void CreateCollation<T>(string name, T state, Func<T, string, string, int> comparison)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException(nameof(name));
}
if (State != ConnectionState.Open)
{
throw new InvalidOperationException($"CallRequiresOpenConnection{nameof(CreateCollation)}");
}
}
/// <summary>
/// Begins a transaction on the connection.
/// </summary>
/// <returns>The transaction.</returns>
public new virtual IoTDBTransaction BeginTransaction()
=> BeginTransaction(IsolationLevel.Unspecified);
/// <summary>
/// Begins a transaction on the connection.
/// </summary>
/// <param name="isolationLevel">The isolation level of the transaction.</param>
/// <returns>The transaction.</returns>
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
=> BeginTransaction(isolationLevel);
/// <summary>
/// Begins a transaction on the connection.
/// </summary>
/// <param name="isolationLevel">The isolation level of the transaction.</param>
/// <returns>The transaction.</returns>
public new virtual IoTDBTransaction BeginTransaction(IsolationLevel isolationLevel)
{
if (State != ConnectionState.Open)
{
throw new InvalidOperationException($"CallRequiresOpenConnection{nameof(BeginTransaction)}");
}
if (Transaction != null)
{
throw new InvalidOperationException($"ParallelTransactionsNotSupported");
}
return Transaction = new IoTDBTransaction(this, isolationLevel);
}
internal string _nowdatabase = string.Empty;
internal bool SelectedDataBase => _nowdatabase != string.Empty;
public override string Database => throw new NotSupportedException();
/// <summary>
/// Changes the current database.
/// </summary>
/// <param name="databaseName">The name of the database to use.</param>
/// <exception cref="PlatformNotSupportedException"></exception>
public override void ChangeDatabase(string databaseName)
{
throw new NotSupportedException();
}
/// <summary>
///
/// </summary>
/// <param name="databaseName"></param>
/// <returns></returns>
/// <exception cref="NotSupportedException"></exception>
public bool DatabaseExists(string databaseName)
{
throw new NotSupportedException();
}
}
}