using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Web.UI.WebControls; using CMS.DataEngine; using CMS.ExtendedControls; using CMS.GlobalHelper; using CMS.IO; using CMS.SettingsProvider; using CMS.UIControls; using CMS.DatabaseHelper; public partial class CMSModules_SystemTables_Controls_Views_SQLEdit : CMSUserControl { #region "Constants" private const int STATE_ALTER_VIEW = 1; // Existing view will be modified private const int STATE_CREATE_VIEW = 2; // New view will be created private const int STATE_ALTER_PROCEDURE = 3; // Existing stored procedure will be modified private const int STATE_CREATE_PROCEDURE = 4; // Mew stored procedure will be created private const string PROCEDURE_CUSTOM_PREFIX = "Proc_Custom_"; private const string VIEW_CUSTOM_PREFIX = "View_Custom_"; #endregion #region "Public properties" /// /// Messages placeholder /// public override MessagesPlaceHolder MessagesPlaceHolder { get { return plcMess; } } /// /// Indicates if control is used on live site. /// public override bool IsLiveSite { get { return base.IsLiveSite; } set { plcMess.IsLiveSite = value; base.IsLiveSite = value; } } /// /// Gets or sets type of database object (view/stored procedure). /// public bool? IsView { get; set; } /// /// Gets or sets name of the database object (view/stored procedure). /// public string ObjectName { get; set; } /// /// Indicates whether parsing code of existing view/procedure failed/ /// public bool FailedToLoad { get; set; } /// /// Gets or sets. /// public bool HideSaveButton { get; set; } /// /// Indicates whether rollback functionality is available for current object. /// public bool RollbackAvailable { get { string fName = null; return SQLScriptExists(ObjectName, ref fName); } } #endregion #region "Private properties" /// /// Current state of editing control (alter view, create view, alter procedure, create procedure). /// private int State { get { return ValidationHelper.GetInteger(ViewState["State"], 0); } set { ViewState["State"] = value; } } #endregion #region "Events" /// /// Event rised when SQL code is successfully saved. /// public event EventHandler OnSaved; #endregion protected void Page_Load(object sender, EventArgs e) { if (!StopProcessing) { // Set max length of object name (according to development mode) if (SettingsKeyProvider.DevelopmentMode) { txtObjName.MaxLength = 128; } else { if (IsView == true) { txtObjName.MaxLength = 128 - VIEW_CUSTOM_PREFIX.Length; } else { txtObjName.MaxLength = 128 - PROCEDURE_CUSTOM_PREFIX.Length; } } btnOk.Visible = !HideSaveButton; if (!String.IsNullOrEmpty(ObjectName) && !FailedToLoad) { if (IsView == true) { if (!ObjectName.StartsWithCSafe(VIEW_CUSTOM_PREFIX, true)) { ShowWarning(GetString("systbl.view.notsystemview"), null, null); } } else if (IsView == false) { if (!ObjectName.StartsWithCSafe(PROCEDURE_CUSTOM_PREFIX, true)) { ShowWarning(GetString("systbl.view.notsystemproc"), null, null); } } } } } #region "Event handlers" /// /// Generate default query. /// protected void btnGenerate_Click(object sender, EventArgs e) { switch (IsView) { case true: txtObjName.Text = (SettingsKeyProvider.DevelopmentMode ? VIEW_CUSTOM_PREFIX : String.Empty) + "MyView"; txtSQLText.Text = "SELECT * FROM CMS_Document"; break; case false: txtObjName.Text = (SettingsKeyProvider.DevelopmentMode ? PROCEDURE_CUSTOM_PREFIX : String.Empty) + "MyProcedure"; txtParams.Text = " @MyIntegerVar int," + Environment.NewLine + " @MyStringVar nvarchar(50)"; txtSQLText.Text = "SELECT 1"; break; default: break; } } /// /// Saves data of edited or new query into DB. /// protected void btnOK_Click(object sender, EventArgs e) { SaveQuery(); } /// /// Initializes the controls. Returns false if parsing code of existing view/procedure failed. /// public bool SetupControl() { return SetupControl(null); } #endregion #region "Private methods" /// /// Initializes the controls. Returns false if parsing code of existing view/procedure failed. /// private bool SetupControl(string code) { bool result = true; if (!String.IsNullOrEmpty(ObjectName) && String.IsNullOrEmpty(code)) { if (IsView != null) { TableManager tm = new TableManager(null); code = tm.GetCode(ObjectName); } } if (IsView == true) { plcGenerate.Visible = true; if (code == null) { lblCreateLbl.Text = "CREATE VIEW " + (!SettingsKeyProvider.DevelopmentMode ? VIEW_CUSTOM_PREFIX : String.Empty); plcGenerate.Visible = true; State = STATE_CREATE_VIEW; } else { lblCreateLbl.Text = "ALTER VIEW"; plcGenerate.Visible = false; string name, body; result = ParseView(code, out name, out body); txtObjName.Enabled = false; txtObjName.ReadOnly = true; txtObjName.Text = name; txtSQLText.Text = body; State = STATE_ALTER_VIEW; } plcParams.Visible = false; lblBegin.Text = "AS"; } else { if (code == null) { lblCreateLbl.Text = "CREATE PROCEDURE " + (!SettingsKeyProvider.DevelopmentMode ? PROCEDURE_CUSTOM_PREFIX : String.Empty); plcGenerate.Visible = true; State = STATE_CREATE_PROCEDURE; } else { plcGenerate.Visible = false; lblCreateLbl.Text = "ALTER PROCEDURE"; string name, param, body; result = ParseProcedure(code, out name, out param, out body); txtObjName.Enabled = false; txtObjName.ReadOnly = true; txtObjName.Text = name; txtParams.Text = param; txtSQLText.Text = body; State = STATE_ALTER_PROCEDURE; } plcParams.Visible = true; lblBegin.Text = "AS
BEGIN"; lblEnd.Text = "END"; } if (!result) { // Parsing code failed => disable all controls DisableControl(txtObjName); DisableControl(txtParams); txtSQLText.EditorMode = EditorModeEnum.Basic; DisableControl(txtSQLText); btnGenerate.Enabled = false; ShowWarning(GetString((IsView == true) ? "systbl.view.parsingfailed" : "systbl.proc.parsingfailed"), null, null); } FailedToLoad = !result; return result; } private void DisableControl(TextBox txt) { txt.ReadOnly = true; txt.Enabled = false; } /// /// Runs edited view or stored procedure. /// public void SaveQuery() { string objName = txtObjName.Text.Trim(); string body = txtSQLText.Text.Trim(); string result = new Validator().NotEmpty(objName, GetString("systbl.viewproc.objectnameempty")) .NotEmpty(body, GetString("systbl.viewproc.bodyempty")).Result; if (String.IsNullOrEmpty(result)) { // Use special prefix for user created views or stored procedures if (!SettingsKeyProvider.DevelopmentMode) { if (State == STATE_CREATE_VIEW) { objName = VIEW_CUSTOM_PREFIX + objName; result = new Validator().IsIdentifier(objName, GetString("systbl.viewproc.viewnotidentifierformat")).Result; } if (State == STATE_CREATE_PROCEDURE) { objName = PROCEDURE_CUSTOM_PREFIX + objName; result = new Validator().IsIdentifier(objName, GetString("systbl.viewproc.procnotidentifierformat")).Result; } } if (String.IsNullOrEmpty(result)) { try { // Retrieve user friendly name string schema, name; ParseName(objName, out schema, out name); // Prepare parameters for stored procedure string param = txtParams.Text.Trim(); string query = ""; TableManager tm = new TableManager(null); switch (State) { case STATE_CREATE_VIEW: query = String.Format("CREATE VIEW {0}\nAS\n{1}", objName, body); // Check if view exists if (tm.ViewExists(objName)) { result = String.Format(GetString("systbl.view.alreadyexists"), objName); } break; case STATE_ALTER_VIEW: query = String.Format("ALTER VIEW {0}\nAS\n{1}", objName, body); break; case STATE_CREATE_PROCEDURE: query = String.Format("CREATE PROCEDURE {0}\n{1}\nAS\nBEGIN\n{2}\nEND\n", objName, param, body); // Check if stored procedure exists if (tm.GetCode(objName) != null) { result = String.Format(GetString("systbl.proc.alreadyexists"), objName); } break; case STATE_ALTER_PROCEDURE: query = String.Format("ALTER PROCEDURE {0}\n{1}\nAS\nBEGIN\n{2}\nEND\n", objName, param, body); break; } if (String.IsNullOrEmpty(result)) { GeneralConnection conn = ConnectionHelper.GetConnection(); conn.ExecuteNonQuery(query, null, QueryTypeEnum.SQLQuery, false); ObjectName = name; if (OnSaved != null) { OnSaved(this, EventArgs.Empty); } } } catch (Exception e) { result = e.Message; } } } // Show error message if any if (!String.IsNullOrEmpty(result)) { ShowError(result); } } /// /// Check if exists SQL script for this object in /App_Data/Install /// /// Name of the object private bool SQLScriptExists(string objName, ref string fileName) { if (!String.IsNullOrEmpty(objName)) { fileName = CMSDatabaseHelper.GetSQLInstallPathToObjects() + "\\" + objName + ".sql"; return File.Exists(fileName); } fileName = null; return TableManager.IsGeneratedSystemView(objName); } /// /// Loads code of original view/stored procedure from SQL script. /// public void Rollback() { if ((State == STATE_ALTER_VIEW) || (State == STATE_ALTER_PROCEDURE)) { string fileName = null; if (SQLScriptExists(ObjectName, ref fileName)) { if (!String.IsNullOrEmpty(fileName)) { // Rollback object from file Regex re = null; if (IsView == true) { re = RegexHelper.GetRegex("\\s*CREATE\\s+VIEW\\s+", RegexOptions.IgnoreCase); } else { re = RegexHelper.GetRegex("\\s*CREATE\\s+PROCEDURE\\s+", RegexOptions.IgnoreCase); } // Load SQL script string query = File.ReadAllText(fileName); // Split query to parts separated by "GO" (trying to find part containing CREATE VIEW or CREATE PROCEDURE) int startingIndex = 0; string partOfQuery = ""; do { int index = query.IndexOfCSafe("GO" + Environment.NewLine, startingIndex, true); if (index == -1) { index = query.IndexOfCSafe(Environment.NewLine + "GO", startingIndex, true); if (index == -1) { index = query.Length; } } // Try to find CREATE VIEW or CREATE PROCEDURE partOfQuery = query.Substring(startingIndex, index - startingIndex).Trim(); if (!String.IsNullOrEmpty(partOfQuery) && re.IsMatch(partOfQuery)) { SetupControl(partOfQuery); txtObjName.Text = ObjectName; break; } startingIndex = index + 3; } while (startingIndex < query.Length); } else { // Rollback object from system string indexes = null; string query = SqlGenerator.GetSystemViewSqlQuery(ObjectName, out indexes); SetupControl(query); } } else { ShowError(GetString("systbl.unabletorollback")); } } } #endregion /// /// Extracts view name and code from SQL query. /// /// Entire SQL query from DB /// View name /// View body private bool ParseView(string query, out string name, out string body) { name = null; body = null; Regex re = RegexHelper.GetRegex(@".*?\s*(CREATE\s+VIEW)\s+(\S+)\s+AS(\s+.*)\s*", RegexOptions.Singleline | RegexOptions.IgnoreCase); if (re.IsMatch(query)) { Match m = re.Match(query); name = m.Groups[2].Value; body = m.Groups[3].Value; return true; } return false; } /// /// Extracts stored procedure name and body from SQL query. /// /// Entire SQL query from DB /// Procedure name /// Parameters /// Procedure body private bool ParseProcedure(string query, out string name, out string param, out string body) { name = null; param = null; body = null; Regex re = RegexHelper.GetRegex(@".*?\s*(CREATE\s+PROCEDURE)\s+(\S+)\s+(.*)\s+(AS\s+BEGIN)(\s+.*)\s+(END)\s*", RegexOptions.Singleline | RegexOptions.IgnoreCase); if (re.IsMatch(query)) { Match m = re.Match(query); name = m.Groups[2].Value; param = m.Groups[3].Value; body = m.Groups[5].Value; return true; } return false; } /// /// Parses DB object name considering database schema. /// /// Object name (view or stored procedure) /// DB schema /// Object name private void ParseName(string objName, out string schema, out string name) { List list = new List(); char current; char? next; int length = objName.Length; int state = 0; string buff = ""; // Loop through object name and extract text in brackets [text1].[text2] // or text separated by period text1.text2 ("]]" is considered to be escape sequence for "]") for (int i = 0; i < length; i++) { current = objName[i]; next = (i < (length - 1)) ? (char?)objName[i + 1] : null; switch (current) { case '[': switch (state) { case 0: state = 1; // Currently in bracket break; case 1: buff += current; break; } break; case ']': if (state == 1) { switch (next) { case ']': // Escape sequence buff += ']'; break; default: state = 0; list.Add(buff); buff = ""; break; } } break; case '.': if (state == 0) { list.Add(buff); buff = String.Empty; } else if (state == 1) { buff += current; } break; default: buff += current; if (next == null) { list.Add(buff); buff = String.Empty; } break; } } schema = null; name = null; if (list.Count > 0) // Find anything? { name = list[list.Count - 1]; if (list.Count > 1) { schema = list[list.Count - 2]; } } } }