using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Common; using FLocal.Core; using FLocal.Core.DB; namespace FLocal.MySQLConnector { public class Connection : IDBConnection { internal readonly IDBTraits traits; private DbConnection connection; private string connectionString; private HashSet transactions; public Connection(string connectionString, IDBTraits traits) { this.traits = traits; this.connectionString = connectionString; this.connection = this.traits.createConnection(this.connectionString); this.connection.Open(); this.transactions = new HashSet(); } internal DbConnection createConnection() { DbConnection connection = this.traits.createConnection(this.connectionString); connection.Open(); return connection; } private List> _LoadByIds(DbCommand command, ITableSpec table, List ids, bool forUpdate) { lock(this) { command.CommandType = System.Data.CommandType.Text; ParamsHolder paramsHolder = new ParamsHolder(); List placeholder = new List(); foreach(string id in ids) { placeholder.Add(this.traits.markParam(paramsHolder.Add(id))); } command.CommandText = "SELECT * FROM " + table.compile(this.traits) + " WHERE " + table.getIdSpec().compile(this.traits) + " IN (" + string.Join(", ", placeholder.ToArray()) + ")" + (forUpdate ? " FOR UPDATE" : ""); //command.Prepare(); foreach(KeyValuePair kvp in paramsHolder.data) { command.AddParameter(kvp.Key, kvp.Value); } Dictionary> rawResult = new Dictionary>(); using(DbDataReader reader = command.ExecuteReader()) { while(reader.Read()) { Dictionary row = new Dictionary(); for(int i=0; i> result = new List>(); foreach(string id in ids) { if(rawResult.ContainsKey(id)) { result.Add(rawResult[id]); } } return result; } } public List> LoadByIds(ITableSpec table, List ids) { lock(this) { using(DbCommand command = this.connection.CreateCommand()) { return this._LoadByIds(command, table, ids, false); } } } private List _LoadIdsByConditions(DbCommand command, ITableSpec table, FLocal.Core.DB.conditions.AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, bool allowHugeLists) { command.CommandType = System.Data.CommandType.Text; var conditionsCompiled = ConditionCompiler.Compile(conditions, this.traits); string queryConditions = ""; if(conditionsCompiled.Key != "") queryConditions = "WHERE " + conditionsCompiled.Key; ParamsHolder paramsHolder = conditionsCompiled.Value; string queryJoins = ""; { if(joins.Length > 0) { throw new NotImplementedException(); } } string querySorts = ""; if(sorts.Length > 0) { List sortParts = new List(); foreach(SortSpec sortSpec in sorts) { if(sortSpec.ascending) { sortParts.Add(sortSpec.column.compile(this.traits) + " ASC"); } else { sortParts.Add(sortSpec.column.compile(this.traits) + " DESC"); } } querySorts = "ORDER BY " + string.Join(", ", sortParts.ToArray()); } string queryMain = "FROM " + table.compile(this.traits) + " " + queryJoins + " " + queryConditions; foreach(KeyValuePair kvp in paramsHolder.data) { command.AddParameter(kvp.Key, kvp.Value); } command.CommandText = "SELECT COUNT(*) " + queryMain; object rawCount; //try { rawCount = command.ExecuteScalar(); //} catch(Npgsql.NpgsqlException e) { //throw new FLocalException("Error while trying to execute " + command.CommandText + ": " + e.Message); //} long count = (long)rawCount; if(count < 1) { diapasone.total = 0; return new List(); } else { diapasone.total = count; if(diapasone.total > 1000 && diapasone.count < 0 && !allowHugeLists) { throw new CriticalException("huge list"); } string queryLimits = ""; if(diapasone.count >= 0) { queryLimits = "LIMIT " + diapasone.count + " OFFSET " + diapasone.start; } command.CommandText = "SELECT " + table.compile(this.traits) + ".* " + queryMain + " " + querySorts + " " + queryLimits; List result = new List(); using(DbDataReader reader = command.ExecuteReader()) { while(reader.Read()) { result.Add(reader.GetValue(0).ToString()); } } return result; } } public List LoadIdsByConditions(ITableSpec table, FLocal.Core.DB.conditions.AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, bool allowHugeLists) { lock(this) { using(DbCommand command = this.connection.CreateCommand()) { return this._LoadIdsByConditions(command, table, conditions, diapasone, joins, sorts, allowHugeLists); } } } public long GetCountByConditions(ITableSpec table, FLocal.Core.DB.conditions.AbstractCondition conditions, JoinSpec[] joins) { lock(this) { using(DbCommand command = this.connection.CreateCommand()) { command.CommandType = System.Data.CommandType.Text; var conditionsCompiled = ConditionCompiler.Compile(conditions, this.traits); string queryConditions = ""; if(conditionsCompiled.Key != "") queryConditions = "WHERE " + conditionsCompiled.Key; ParamsHolder paramsHolder = conditionsCompiled.Value; string queryJoins = ""; { if(joins.Length > 0) { throw new NotImplementedException(); } } command.CommandText = "SELECT COUNT(*) " + "FROM " + table.compile(this.traits) + " " + queryJoins + " " + queryConditions; foreach(KeyValuePair kvp in paramsHolder.data) { command.AddParameter(kvp.Key, kvp.Value); } object rawCount = command.ExecuteScalar(); long count = (long)rawCount; return count; } } } public FLocal.Core.DB.Transaction beginTransaction(System.Data.IsolationLevel iso) { lock(this) { Transaction transaction = new Transaction(this, iso); try { this.transactions.Add(transaction); } catch(Exception e) { transaction.Dispose(); throw e; } return transaction; } } public void lockTable(FLocal.Core.DB.Transaction _transaction, ITableSpec table) { Transaction transaction = (Transaction)_transaction; lock(transaction) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) { command.Transaction = transaction.sqltransaction; command.CommandType = System.Data.CommandType.Text; command.CommandText = "LOCK TABLE " + table.compile(this.traits); command.ExecuteNonQuery(); } } } public void lockRow(FLocal.Core.DB.Transaction _transaction, ITableSpec table, string id) { Transaction transaction = (Transaction)_transaction; lock(transaction) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) { command.Transaction = transaction.sqltransaction; command.CommandType = System.Data.CommandType.Text; command.CommandText = "SELECT * FROM " + table.compile(this.traits) + " where " + table.getIdSpec().compile(this.traits) + " = " + this.traits.markParam("id") + " FOR UPDATE"; command.AddParameter("id", id); command.ExecuteNonQuery(); } } } public List> LoadByIds(FLocal.Core.DB.Transaction _transaction, ITableSpec table, List ids) { Transaction transaction = (Transaction)_transaction; lock(transaction) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) { command.Transaction = transaction.sqltransaction; return this._LoadByIds(command, table, ids, true); } } } public List LoadIdsByConditions(FLocal.Core.DB.Transaction _transaction, ITableSpec table, FLocal.Core.DB.conditions.AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, bool allowHugeLists) { Transaction transaction = (Transaction)_transaction; lock(this) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) { command.Transaction = transaction.sqltransaction; return this._LoadIdsByConditions(command, table, conditions, diapasone, joins, sorts, allowHugeLists); } } } public void update(FLocal.Core.DB.Transaction _transaction, ITableSpec table, string id, Dictionary data) { Transaction transaction = (Transaction)_transaction; lock(transaction) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) { List updates = new List(); ParamsHolder paramsholder = new ParamsHolder(); foreach(KeyValuePair kvp in data) { updates.Add(this.traits.escapeIdentifier(kvp.Key) + " = " + this.traits.markParam(paramsholder.Add(kvp.Value))); } command.Transaction = transaction.sqltransaction; command.CommandType = System.Data.CommandType.Text; command.CommandText = "UPDATE " + table.compile(traits) + " set " + String.Join(", ", updates.ToArray()) + " where " + table.getIdSpec().compile(this.traits) + " = " + this.traits.markParam("id"); command.AddParameter("id", id); foreach(KeyValuePair kvp in paramsholder.data) { command.AddParameter(kvp.Key, kvp.Value); } // throw new CriticalException(command.CommandText + "; parameters: " + string.Join(", ", (from DbParameter parameter in command.Parameters select parameter.ParameterName + "='" + parameter.Value.ToString() + "'").ToArray())); command.ExecuteNonQuery(); } } } public string insert(FLocal.Core.DB.Transaction _transaction, ITableSpec table, Dictionary data) { Transaction transaction = (Transaction)_transaction; lock(transaction) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) { List updates = new List(); List updatesPlaceholders = new List(); ParamsHolder paramsholder = new ParamsHolder(); foreach(KeyValuePair kvp in data) { updates.Add(this.traits.escapeIdentifier(kvp.Key)); updatesPlaceholders.Add(this.traits.markParam(paramsholder.Add(kvp.Value))); } command.Transaction = transaction.sqltransaction; command.CommandType = System.Data.CommandType.Text; command.CommandText = "INSERT INTO " + table.compile(this.traits) + " (" + String.Join(", ", updates.ToArray()) + ") VALUES (" + String.Join(", ", updatesPlaceholders.ToArray()) + ")"; foreach(KeyValuePair kvp in paramsholder.data) { command.AddParameter(kvp.Key, kvp.Value); } command.ExecuteNonQuery(); if(data.ContainsKey(table.idName)) return data[table.idName]; return this.traits.LastInsertId(command, table).ToString(); } } } public void delete(FLocal.Core.DB.Transaction _transaction, ITableSpec table, string id) { Transaction transaction = (Transaction)_transaction; lock(transaction) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) { command.Transaction = transaction.sqltransaction; command.CommandType = System.Data.CommandType.Text; command.CommandText = "DELETE FROM " + table.compile(traits) + " where " + table.getIdSpec().compile(this.traits) + " = " + this.traits.markParam("id"); command.AddParameter("id", id); command.ExecuteNonQuery(); } } } public void Dispose() { lock(this) { foreach(Transaction transaction in this.transactions) { if(!transaction.finalizedImpl) { throw new CriticalException("Trying to close db connection while there are open transactions"); } } this.transactions.Clear(); this.transactions = null; this.connection.Close(); this.connection.Dispose(); this.connection = null; } } } }