public static bool IsSingleByteType(COLUMN_TYPES colType) { COLUMN_TYPES[] asingleByteTypes = new COLUMN_TYPES[] { COLUMN_TYPES.BIT, COLUMN_TYPES.SINGLE_CHAR, COLUMN_TYPES.TINYINT }; return asingleByteTypes.Contains(colType); }
public static bool IsSingleByteType(COLUMN_TYPES colType) { COLUMN_TYPES[] asingleByteTypes = new COLUMN_TYPES[] { COLUMN_TYPES.BIT, COLUMN_TYPES.SINGLE_CHAR, COLUMN_TYPES.TINYINT }; return(asingleByteTypes.Contains(colType)); }
public COLUMN_TYPES getColType(int intColIndex) { COLUMN_TYPES colType = (COLUMN_TYPES)Activator.CreateInstance(typeof(COLUMN_TYPES)); // seems to be a slick way to get "0"; // the default val for any enum // see... // http://stackoverflow.com/a/8927369/1028230 // http://stackoverflow.com/a/529937/1028230 -- "The default for an enum (in fact, any value type) is 0 -- even if that is not a valid value for that enum. It cannot be changed." colType = this._columns[intColIndex].colType; return(colType); }
// A very tedious function. private int _getDefaultLengthForType(COLUMN_TYPES colType) { int intReturn = -1; switch (colType) { case COLUMN_TYPES.AUTOINCREMENT: intReturn = 4; break; case COLUMN_TYPES.BIT: intReturn = 1; break; case COLUMN_TYPES.CHAR: intReturn = 20; break; case COLUMN_TYPES.DATETIME: intReturn = 8; break; // currently treated the same. case COLUMN_TYPES.DECIMAL: case COLUMN_TYPES.FLOAT: intReturn = 10; break; case COLUMN_TYPES.INT: intReturn = 4; // 4*8 = Int32 break; case COLUMN_TYPES.TINYINT: intReturn = 1; break; case COLUMN_TYPES.SINGLE_CHAR: intReturn = 1; break; default: throw new Exception("Column Type has no default length; please declare one: " + colType.ToString()); } return(intReturn); }
public static void ProcessRows( ref DataTable dtWithCols, TableContext table, CommandParts commandParts ) { string strWhere = commandParts.strWhere; List<Comparison> lstWhereConditions = _CreateWhereConditions(strWhere, table); // TODO: Really need to design a legitimate table locking system. int delayFactor = 1; try { using (BinaryReader b = new BinaryReader(File.Open(table.strTableFileLoc, FileMode.Open))) { int intRowCount = table.intFileLength / table.intRowLength; b.BaseStream.Seek(2 * table.intRowLength, SeekOrigin.Begin); // TODO: Code more defensively in case it's somehow not the right/minimum length for (int i = 2; i < intRowCount; i++) { byte[] abytRow = b.ReadBytes(table.intRowLength); bool bMatchingRow = true; // Check and make sure this is an active row, and has // the standard row lead byte, 0x11. If not, the row // should not be read. // I'm going to switch this to make it more defensive // and a little easier to follow. switch (abytRow[0]) { case 0x88: // DELETED bMatchingRow = false; break; case 0x11: // ACTIVE // Find if the WHERE clause says to exclude this row. foreach (Comparison comparison in lstWhereConditions) { // For now, we're (somewhat clumsily) processing INs as lots of small ORs. // And no, we're not actually supporting the OR statement in a regular WHERE yet. if (comparison is CompoundComparison) { bool bInKeeper = false; // Could use a lot more indexed logic here, but that'll need to be // an extension to this package to keep the logic simple. // This is a painful, bullheaded Moore's comparison. foreach (Comparison compInner in ((CompoundComparison)comparison).lstComparisons) { if (_ComparisonEngine(compInner, abytRow)) { bInKeeper = true; break; } } bMatchingRow = bMatchingRow && bInKeeper; } else { bMatchingRow = bMatchingRow && _ComparisonEngine(comparison, abytRow); } } break; default: throw new Exception("Unexpected row state in SELECT: " + abytRow[0]); } if (bMatchingRow) { switch (commandParts.commandType) { case CommandParts.COMMAND_TYPES.SELECT: #region SELECT Dictionary<string, string> dictFuzzyToColName = new Dictionary<string, string>(commandParts.dictFuzzyToColNameMappings); // resets with each row. DataRow row = dtWithCols.NewRow(); foreach (Column mCol in commandParts.acolInSelect) { byte[] abytCol = new byte[mCol.intColLength]; Array.Copy(abytRow, mCol.intColStart, abytCol, 0, mCol.intColLength); //Console.WriteLine(System.Text.Encoding.Default.GetString(abytCol)); // now translate/cast the value to the column in the row. // OLD: row[OperativeName(mCol.strColName, dictColNameMapping)] = Router.routeMe(mCol).toNative(abytCol); // foreach b/c we're supporting multiple calls to the same col in a SELECT now. foreach (DataColumn dc in dtWithCols.Columns) { // See if we should use this column's (mCol's) value with this DataColumn. if (dictFuzzyToColName.ContainsValue(mCol.strColName) || mCol.strColName.Equals(dc.ColumnName)) { // If so, see if there's a fuzzy name mapped for this column. string strColName = GetFuzzyNameIfExists(mCol.strColName, dictFuzzyToColName); row[strColName] = Router.routeMe(mCol).toNative(abytCol); // If we had a fuzzy name, remove from the dictionary so we don't dupe it. if (dictFuzzyToColName.ContainsKey(strColName)) { dictFuzzyToColName.Remove(strColName); } } } } dtWithCols.Rows.Add(row); #endregion SELECT break; case CommandParts.COMMAND_TYPES.UPDATE: #region UPDATE // kludge for fuzzy names: // (This should be a one-way process, so I don't think having the logic // in this cruddy a place is a huge problem that'll cause wasted // resources; it's just having me rethink fuzzy names in general.) Dictionary<string, string> dictLaunderedUpdateVals = new Dictionary<string,string>(); foreach (string key in commandParts.dictUpdateColVals.Keys) { dictLaunderedUpdateVals.Add(table.getRawColName(key), commandParts.dictUpdateColVals[key]); } foreach (Column mCol in table.getColumns()) { Column colToPullValueFrom = null; string strUpdateValueModifier = string.Empty; if (dictLaunderedUpdateVals.ContainsKey(mCol.strColName)) { // Column needs updating; take values from update byte[] abytNewColVal = null; // Will hold "raw" value. Might not be the full column length. // Check to see if we're updating using another column from the same row or a value. // TODO: Performance here should be crappy. Create a mapping of col names & Cols for // in-statement column value transfers. ie, "UPDATE table1 SET col1 = col2 WHERE col1 = 'update me';" string valueAsString = dictLaunderedUpdateVals[mCol.strColName]; // Check for operators inside of update values. // TODO: Handle strings with operators, but then that's what CONCAT is for. // See PIPES_AS_CONCAT in MySQL for more fun. (Note that SQL Server does // allow string concat via `+`.) // // TODO: Note that tabs in the phrase (though strange) should be legit. // The current state of the code will choke on them, however. // // NOTE: I'm going to slowly refactor to ConstructValue as I add the operation // functions to the serializers. So right now I've only got IntSerializer ready. // (... but I want to check this in instead of stash). COLUMN_TYPES[] validValueModiferTypes = new COLUMN_TYPES[] { COLUMN_TYPES.INT }; if (validValueModiferTypes.Contains(mCol.colType)) { // New method that allows composition update clauses (eg, `col1 + 4 - col2`) abytNewColVal = CompositeColumnValueModifier.ConstructValue(mCol, valueAsString, abytRow, table); } else { // Old method to update value (no composite clauses). colToPullValueFrom = table.getColumnByName(valueAsString); if (null != colToPullValueFrom) { if (mCol.intColLength < colToPullValueFrom.intColLength || !CompositeColumnValueModifier.ColsAreCompatible(mCol, colToPullValueFrom)) { throw new Exception("UPDATE attempted to update with a value that was potentially too large or with columns of incompatible types."); } abytNewColVal = new byte[colToPullValueFrom.intColLength]; Array.Copy(abytRow, colToPullValueFrom.intColStart, abytNewColVal, 0, colToPullValueFrom.intColLength); } else { BaseSerializer serializer = Router.routeMe(mCol); abytNewColVal = serializer.toByteArray(dictLaunderedUpdateVals[mCol.strColName]); } } // double check that the serializer at least // gave you a value that's the right length so // that everything doesn't go to heck (moved where // that was previously checked into the serializers) if (abytNewColVal.Length != mCol.intColLength) { throw new Exception("Improperly lengthed field from serializer (UPDATE): " + mCol.strColName); } // keep in mind that column.intColLength should always match abytColValue.Length. While I'm // testing, I'm going to put in this check, but at some point, you should be confident enough // to consider removing this check. if (abytNewColVal.Length != mCol.intColLength) { throw new Exception("Surprising value and column length mismatch"); } Buffer.BlockCopy(abytNewColVal, 0, abytRow, mCol.intColStart, abytNewColVal.Length); } // else don't touch what's in the row; it's not an updated colum } b.BaseStream.Seek(-1 * table.intRowLength, SeekOrigin.Current); b.BaseStream.Write(abytRow, 0, abytRow.Length); #endregion UPDATE break; case CommandParts.COMMAND_TYPES.DELETE: byte[] abytErase = new byte[table.intRowLength]; // should be initialized to zeroes. // at least to test, I'm going to write it all over with 0x88s. for (int j = 0; j < table.intRowLength; j++) { abytErase[j] = 0x88; } // move pointer back to the first byte of this row. b.BaseStream.Seek(-1 * table.intRowLength, SeekOrigin.Current); b.BaseStream.Write(abytErase, 0, abytErase.Length); break; default: throw new Exception("Unhandled command type in WhereProcessor: " + commandParts.commandType); } } } // eo for loop i < intRowCount } // eo using statement for the binary reader. } catch (IOException) { delayFactor = delayFactor * 2; if (delayFactor > (3 * 60 * 1000)) { throw new Exception("Statement timeout: " + commandParts.strOriginal); } Thread.Sleep(delayFactor * 200); //org.rufwork.mooresDb.SqlDbSharpLogger.LogMessage(table.strTableName + ".mdbf is locked. Waiting " + delayFactor + " millis to try again.", "WhereProcessor.ProcessRows"); } // nothing to return -- dt was passed by ref. }
/// <summary> /// Processes a CREATE TABLE statement. /// Throws any Exception on any failure. /// </summary> /// <param name="strSql"></param> public object executeStatement(string strSql) { string strErr = ""; Match createTableMatch = Regex.Match(strSql, @"^CREATE\s*TABLE\s*`?\w*`?\s*\(", RegexOptions.IgnoreCase); List <string> lstRawNames = new List <string>(); if (createTableMatch.Success) { string strTableName = Regex.Replace(createTableMatch.Groups[0].Value, @"\r\n?|\n", ""); // remove newlines with http://stackoverflow.com/a/8196219/1028230 strTableName = strTableName.Substring(0, strTableName.ToLower().IndexOf("(")); strTableName = strTableName.Substring(strTableName.ToLower().IndexOf("table") + 5).Trim().Trim('`'); if (null != _database.getTableByName(strTableName)) { strErr += "Table " + strTableName + " already exists.\n"; } else { // Else this IS a legitimate location for a table file. Begin. // Store table loc in TableContext _table = new TableContext(); // initialize rows with the 11 byte. _lstByteDataTypeRow.Add(0x11); _lstByteColNames.Add(0x11); string strColInfo = strSql.Substring(strSql.IndexOf("(") + 1); // get rid of everything up until the first open parens. strColInfo = strColInfo.Substring(0, strColInfo.LastIndexOf(")")); // peel off the last closed parens. string[] astrSections = Regex.Split(strColInfo, ","); for (int i = 0; i < astrSections.Length; i++) { COLUMN_TYPES?colType = null; // This really should never be null after running through the code. It'll throw an exception first. string strColName = ""; int intFieldLength = -1; string strNextColumnInfo = astrSections[i].Trim(); // If we're defining a primary key, which we don't support (yet, if ever), skip the line. // Else do the normal thing. if (strNextColumnInfo.StartsWith("PRIMARY", StringComparison.CurrentCultureIgnoreCase)) { SqlDbSharpLogger.LogMessage("Primary key creation is currently ignored: " + strNextColumnInfo, "CreateTable executeStatement"); } else { string[] astrColInfo = strNextColumnInfo.StringToNonWhitespaceTokens2(); if (astrColInfo.Length < 2) { strErr += "Illegal column defintion; table not created: " + string.Join(":", astrColInfo) + "#\n"; } else { //===================== //======= DEBUG ======= //===================== if (MainClass.bDebug) { for (int j = 0; j < astrColInfo.Length; j++) { SqlDbSharpLogger.LogMessage(j + " :: " + astrColInfo[j], "Create table execute statement"); } } //====================== //====================== if (3 <= astrColInfo.Length) { int intLength; if (int.TryParse(astrColInfo[2], out intLength)) { if (4369 == intLength) { throw new Exception("Idiosyncratically, column lengths of [exactly] 4369 are not allowed. " + astrColInfo[1]); } intFieldLength = intLength; } else { // We're going to step up from defaulting to a length of 20 in each case to // defining a default length for each column data type. intFieldLength = -1; } } // every column declaration has already been checked to ensure it has at least two entries (checked above) // TODO: Check for statements expecting the default length but with modifiers, like // `id` INT NOT NULL AUTO_INCREMENT DEFAULT NULL, // This is handled with AUTO_INCREMENT because NOT NULL is thrown out as if it had the length. // Which is to say, NOT NULL isn't passed along as part of the modifier string. strColName = astrColInfo[0].Trim('`'); string strModifier = null; if (astrColInfo.Length > 3) { strModifier = string.Join(" ", astrColInfo, 3, astrColInfo.Length - 3); } colType = InfrastructureUtils.colTypeFromString(astrColInfo[1], intFieldLength, strModifier); if (null == colType) { strErr += "Illegal/Supported column type: " + astrColInfo[1] + "\n"; } else { COLUMN_TYPES colTypeCleaned = (COLUMN_TYPES)colType; // got to be a better way to launder a nullable. if (intFieldLength < 0) { intFieldLength = _getDefaultLengthForType(colTypeCleaned); } string strRawName = strColName.Length > intFieldLength?strColName.Substring(0, intFieldLength) : strColName; IEnumerable <string> coveredNames = lstRawNames.Where(name => name.StartsWith(strRawName, StringComparison.CurrentCultureIgnoreCase) || strRawName.StartsWith(name, StringComparison.CurrentCultureIgnoreCase)); if (coveredNames.Count() > 0) { throw new Exception(string.Format(@"Field names would ""cover"" each other: `{0}` and `{1}`", strRawName, coveredNames.First())); } lstRawNames.Add(strRawName); _createColumn(strColName, colTypeCleaned, intFieldLength); } if (!strErr.Equals("")) { break; } } } // end check for unsupported directives (like defining a primary key) } // eo table column creation for loop if (MainClass.bDebug) { string strDebug = string.Empty; for (int j = 0; j < _lstByteDataTypeRow.Count; j++) { strDebug += "0x" + _lstByteDataTypeRow[j].ToString("X2") + ", \n"; } strDebug += "\n\n"; for (int j = 0; j < _lstByteColNames.Count; j++) { strDebug += "0x" + _lstByteColNames[j].ToString("X2") + ", \n"; } strDebug += "\n"; strDebug += _table.strTableFileLoc + "\n"; SqlDbSharpLogger.LogMessage(strDebug, "Create table execute statement"); } // TODO: Instead of writing bytes here, I should probably create a list of column objects, // with name, length, and COLUMN_TYPE for each, then let the table manager worry about // bytes and specific implmentations. _table.writeMetadataRowsAndPrepareNewTable(_lstByteDataTypeRow, _lstByteColNames, strTableName, _database.strDbLoc); _database.addNewTable(_table); } // eo table exists check. } // eo createTableMatch.Success regex check else { strErr += "SYNTAX ERROR: Illegal Create Table Statement" + System.Environment.NewLine; // go ahead and throw specific error type. throw new SyntaxException(strErr); } strErr = strErr.Equals("") ? "Table created successfully." : "Create table error" + System.Environment.NewLine + strErr; return(strErr); }
private void _createColumn(string strName, COLUMN_TYPES colType, int intLength) { List <byte> lstBytToAdd = new List <byte>(); lstBytToAdd.Add((byte)colType); if (Column.IsSingleByteType(colType)) { if (intLength != 1) { throw new Exception("cannot create column. Single byte column type with length greater than 1: " + strName + " :: " + intLength); // TODO: Consider string prefixes for all exceptions in each class. } lstBytToAdd.Add(0x11); // bookend with 11s // Otherwise, that's it. We've got the length implicitly in the colType we wrote, above. // (and there's no space to add a length; the column only has one byte of metadata that // we wrote in with .Add((byte)colType), above. } else if (COLUMN_TYPES.INT == colType && intLength != 4) { throw new Exception("For now, INTs must be 4 bytes in length."); } else if (COLUMN_TYPES.AUTOINCREMENT == colType && 4 != intLength) { throw new Exception("For now, autoincrement columns must be four byte integers."); } else { if (COLUMN_TYPES.AUTOINCREMENT == colType) { // Keeping the length constant makes the rest of this setup trivial. lstBytToAdd.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x11 }); } else { // Else determine and write the column's metadata. byte[] abyteIntLength = Utils.IntToByteArray(intLength); int i = 0; int intPaddingLength = intLength - ((abyteIntLength.Length - i) + 1); if (MainClass.bDebug) { Console.WriteLine("intLength: " + intLength + " i: " + i + " bytes for length: " + (abyteIntLength.Length - i) + " intPaddingLength: " + intPaddingLength); } while (i < abyteIntLength.Length) { //Console.WriteLine("adding: " + abyteInt[i].ToString("X")); lstBytToAdd.Add(abyteIntLength[i++]); } // now pad out the space and put an 11 on the end. while (intPaddingLength > 0) { lstBytToAdd.Add(0x00); intPaddingLength--; } lstBytToAdd.Add(0x11); // bookend with 11s } } _lstByteDataTypeRow = _lstByteDataTypeRow.Concat(lstBytToAdd).ToList(); //================================================================ // Column names row //================================================================ // Write out row of column names. Watch out for insane kludges. // TODO: Look out for duplicate column names and for masking longer // names when shortened. if (intLength > strName.Length) { foreach (byte bytTemp in strName.ToCharArray()) { _lstByteColNames.Add(bytTemp); } for (int j = strName.Length; j < intLength; j++) { _lstByteColNames.Add(0x00); } } else { string strShortenedName = strName.Substring(0, intLength); Console.WriteLine("TODO: Write out a file with shortened name mappings just to make things easier."); foreach (byte bytTemp in strShortenedName.ToCharArray()) { _lstByteColNames.Add(bytTemp); } } _lstByteColNames.Add(0x11); }
public Column(string name, COLUMN_TYPES colType, int intColStart, int intColLength) { this.strColName = name; this.intColStart = intColStart; this.intColLength = intColLength; }
// A very tedious function. private int _getDefaultLengthForType(COLUMN_TYPES colType) { int intReturn = -1; switch (colType) { case COLUMN_TYPES.AUTOINCREMENT: intReturn = 4; break; case COLUMN_TYPES.BIT: intReturn = 1; break; case COLUMN_TYPES.CHAR: intReturn = 20; break; case COLUMN_TYPES.DATETIME: intReturn = 8; break; // currently treated the same. case COLUMN_TYPES.DECIMAL: case COLUMN_TYPES.FLOAT: intReturn = 10; break; case COLUMN_TYPES.INT: intReturn = 4; // 4*8 = Int32 break; case COLUMN_TYPES.TINYINT: intReturn = 1; break; case COLUMN_TYPES.SINGLE_CHAR: intReturn = 1; break; default: throw new Exception("Column Type has no default length; please declare one: " + colType.ToString()); } return intReturn; }
private void _createColumn(string strName, COLUMN_TYPES colType, int intLength) { List<byte> lstBytToAdd = new List<byte>(); lstBytToAdd.Add((byte)colType); if (Column.IsSingleByteType(colType)) { if (intLength != 1) { throw new Exception("cannot create column. Single byte column type with length greater than 1: " + strName + " :: " + intLength); // TODO: Consider string prefixes for all exceptions in each class. } lstBytToAdd.Add(0x11); // bookend with 11s // Otherwise, that's it. We've got the length implicitly in the colType we wrote, above. // (and there's no space to add a length; the column only has one byte of metadata that // we wrote in with .Add((byte)colType), above. } else if (COLUMN_TYPES.INT == colType && intLength != 4) { throw new Exception("For now, INTs must be 4 bytes in length."); } else if (COLUMN_TYPES.AUTOINCREMENT == colType && 4 != intLength) { throw new Exception("For now, autoincrement columns must be four byte integers."); } else { if (COLUMN_TYPES.AUTOINCREMENT == colType) { // Keeping the length constant makes the rest of this setup trivial. lstBytToAdd.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x11 }); } else { // Else determine and write the column's metadata. byte[] abyteIntLength = Utils.IntToByteArray(intLength); int i = 0; int intPaddingLength = intLength - ((abyteIntLength.Length - i) + 1); if (MainClass.bDebug) { Console.WriteLine("intLength: " + intLength + " i: " + i + " bytes for length: " + (abyteIntLength.Length - i) + " intPaddingLength: " + intPaddingLength); } while (i < abyteIntLength.Length) { //Console.WriteLine("adding: " + abyteInt[i].ToString("X")); lstBytToAdd.Add(abyteIntLength[i++]); } // now pad out the space and put an 11 on the end. while (intPaddingLength > 0) { lstBytToAdd.Add(0x00); intPaddingLength--; } lstBytToAdd.Add(0x11); // bookend with 11s } } _lstByteDataTypeRow = _lstByteDataTypeRow.Concat(lstBytToAdd).ToList(); //================================================================ // Column names row //================================================================ // Write out row of column names. Watch out for insane kludges. // TODO: Look out for duplicate column names and for masking longer // names when shortened. if (intLength > strName.Length) { foreach (byte bytTemp in strName.ToCharArray()) { _lstByteColNames.Add(bytTemp); } for (int j = strName.Length; j < intLength; j++) { _lstByteColNames.Add(0x00); } } else { string strShortenedName = strName.Substring(0, intLength); Console.WriteLine("TODO: Write out a file with shortened name mappings just to make things easier."); foreach (byte bytTemp in strShortenedName.ToCharArray()) { _lstByteColNames.Add(bytTemp); } } _lstByteColNames.Add(0x11); }
public static void ProcessRows( ref DataTable dtWithCols, TableContext table, CommandParts commandParts ) { string strWhere = commandParts.strWhere; List <Comparison> lstWhereConditions = _CreateWhereConditions(strWhere, table); // TODO: Really need to design a legitimate table locking system. int delayFactor = 1; try { using (BinaryReader b = new BinaryReader(File.Open(table.strTableFileLoc, FileMode.Open))) { int intRowCount = table.intFileLength / table.intRowLength; b.BaseStream.Seek(2 * table.intRowLength, SeekOrigin.Begin); // TODO: Code more defensively in case it's somehow not the right/minimum length for (int i = 2; i < intRowCount; i++) { byte[] abytRow = b.ReadBytes(table.intRowLength); bool bMatchingRow = true; // Check and make sure this is an active row, and has // the standard row lead byte, 0x11. If not, the row // should not be read. // I'm going to switch this to make it more defensive // and a little easier to follow. switch (abytRow[0]) { case 0x88: // DELETED bMatchingRow = false; break; case 0x11: // ACTIVE // Find if the WHERE clause says to exclude this row. foreach (Comparison comparison in lstWhereConditions) { // For now, we're (somewhat clumsily) processing INs as lots of small ORs. // And no, we're not actually supporting the OR statement in a regular WHERE yet. if (comparison is CompoundComparison) { bool bInKeeper = false; // Could use a lot more indexed logic here, but that'll need to be // an extension to this package to keep the logic simple. // This is a painful, bullheaded Moore's comparison. foreach (Comparison compInner in ((CompoundComparison)comparison).lstComparisons) { if (_ComparisonEngine(compInner, abytRow)) { bInKeeper = true; break; } } bMatchingRow = bMatchingRow && bInKeeper; } else { bMatchingRow = bMatchingRow && _ComparisonEngine(comparison, abytRow); } } break; default: throw new Exception("Unexpected row state in SELECT: " + abytRow[0]); } if (bMatchingRow) { switch (commandParts.commandType) { case CommandParts.COMMAND_TYPES.SELECT: #region SELECT Dictionary <string, string> dictFuzzyToColName = new Dictionary <string, string>(commandParts.dictFuzzyToColNameMappings); // resets with each row. DataRow row = dtWithCols.NewRow(); foreach (Column mCol in commandParts.acolInSelect) { byte[] abytCol = new byte[mCol.intColLength]; Array.Copy(abytRow, mCol.intColStart, abytCol, 0, mCol.intColLength); //Console.WriteLine(System.Text.Encoding.Default.GetString(abytCol)); // now translate/cast the value to the column in the row. // OLD: row[OperativeName(mCol.strColName, dictColNameMapping)] = Router.routeMe(mCol).toNative(abytCol); // foreach b/c we're supporting multiple calls to the same col in a SELECT now. foreach (DataColumn dc in dtWithCols.Columns) { // See if we should use this column's (mCol's) value with this DataColumn. if (dictFuzzyToColName.ContainsValue(mCol.strColName) || mCol.strColName.Equals(dc.ColumnName)) { // If so, see if there's a fuzzy name mapped for this column. string strColName = GetFuzzyNameIfExists(mCol.strColName, dictFuzzyToColName); row[strColName] = Router.routeMe(mCol).toNative(abytCol); // If we had a fuzzy name, remove from the dictionary so we don't dupe it. if (dictFuzzyToColName.ContainsKey(strColName)) { dictFuzzyToColName.Remove(strColName); } } } } dtWithCols.Rows.Add(row); #endregion SELECT break; case CommandParts.COMMAND_TYPES.UPDATE: #region UPDATE // kludge for fuzzy names: // (This should be a one-way process, so I don't think having the logic // in this cruddy a place is a huge problem that'll cause wasted // resources; it's just having me rethink fuzzy names in general.) Dictionary <string, string> dictLaunderedUpdateVals = new Dictionary <string, string>(); foreach (string key in commandParts.dictUpdateColVals.Keys) { dictLaunderedUpdateVals.Add(table.getRawColName(key), commandParts.dictUpdateColVals[key]); } foreach (Column mCol in table.getColumns()) { Column colToPullValueFrom = null; string strUpdateValueModifier = string.Empty; if (dictLaunderedUpdateVals.ContainsKey(mCol.strColName)) { // Column needs updating; take values from update byte[] abytNewColVal = null; // Will hold "raw" value. Might not be the full column length. // Check to see if we're updating using another column from the same row or a value. // TODO: Performance here should be crappy. Create a mapping of col names & Cols for // in-statement column value transfers. ie, "UPDATE table1 SET col1 = col2 WHERE col1 = 'update me';" string valueAsString = dictLaunderedUpdateVals[mCol.strColName]; // Check for operators inside of update values. // TODO: Handle strings with operators, but then that's what CONCAT is for. // See PIPES_AS_CONCAT in MySQL for more fun. (Note that SQL Server does // allow string concat via `+`.) // // TODO: Note that tabs in the phrase (though strange) should be legit. // The current state of the code will choke on them, however. // // NOTE: I'm going to slowly refactor to ConstructValue as I add the operation // functions to the serializers. So right now I've only got IntSerializer ready. // (... but I want to check this in instead of stash). COLUMN_TYPES[] validValueModiferTypes = new COLUMN_TYPES[] { COLUMN_TYPES.INT }; if (validValueModiferTypes.Contains(mCol.colType)) { // New method that allows composition update clauses (eg, `col1 + 4 - col2`) abytNewColVal = CompositeColumnValueModifier.ConstructValue(mCol, valueAsString, abytRow, table); } else { // Old method to update value (no composite clauses). colToPullValueFrom = table.getColumnByName(valueAsString); if (null != colToPullValueFrom) { if (mCol.intColLength < colToPullValueFrom.intColLength || !CompositeColumnValueModifier.ColsAreCompatible(mCol, colToPullValueFrom)) { throw new Exception("UPDATE attempted to update with a value that was potentially too large or with columns of incompatible types."); } abytNewColVal = new byte[colToPullValueFrom.intColLength]; Array.Copy(abytRow, colToPullValueFrom.intColStart, abytNewColVal, 0, colToPullValueFrom.intColLength); } else { BaseSerializer serializer = Router.routeMe(mCol); abytNewColVal = serializer.toByteArray(dictLaunderedUpdateVals[mCol.strColName]); } } // double check that the serializer at least // gave you a value that's the right length so // that everything doesn't go to heck (moved where // that was previously checked into the serializers) if (abytNewColVal.Length != mCol.intColLength) { throw new Exception("Improperly lengthed field from serializer (UPDATE): " + mCol.strColName); } // keep in mind that column.intColLength should always match abytColValue.Length. While I'm // testing, I'm going to put in this check, but at some point, you should be confident enough // to consider removing this check. if (abytNewColVal.Length != mCol.intColLength) { throw new Exception("Surprising value and column length mismatch"); } Buffer.BlockCopy(abytNewColVal, 0, abytRow, mCol.intColStart, abytNewColVal.Length); } // else don't touch what's in the row; it's not an updated colum } b.BaseStream.Seek(-1 * table.intRowLength, SeekOrigin.Current); b.BaseStream.Write(abytRow, 0, abytRow.Length); #endregion UPDATE break; case CommandParts.COMMAND_TYPES.DELETE: byte[] abytErase = new byte[table.intRowLength]; // should be initialized to zeroes. // at least to test, I'm going to write it all over with 0x88s. for (int j = 0; j < table.intRowLength; j++) { abytErase[j] = 0x88; } // move pointer back to the first byte of this row. b.BaseStream.Seek(-1 * table.intRowLength, SeekOrigin.Current); b.BaseStream.Write(abytErase, 0, abytErase.Length); break; default: throw new Exception("Unhandled command type in WhereProcessor: " + commandParts.commandType); } } } // eo for loop i < intRowCount } // eo using statement for the binary reader. } catch (IOException) { delayFactor = delayFactor * 2; if (delayFactor > (3 * 60 * 1000)) { throw new Exception("Statement timeout: " + commandParts.strOriginal); } Thread.Sleep(delayFactor * 200); //org.rufwork.mooresDb.SqlDbSharpLogger.LogMessage(table.strTableName + ".mdbf is locked. Waiting " + delayFactor + " millis to try again.", "WhereProcessor.ProcessRows"); } // nothing to return -- dt was passed by ref. }