Example #1
0
 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);
 }
Example #2
0
 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));
 }
Example #3
0
        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);
        }
Example #4
0
        // 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);
        }
Example #5
0
        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.
        }
Example #6
0
        /// <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);
        }
Example #7
0
        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);
        }
Example #8
0
 public Column(string name, COLUMN_TYPES colType, int intColStart, int intColLength)
 {
     this.strColName = name;
     this.intColStart = intColStart;
     this.intColLength = intColLength;
 }
Example #9
0
 public Column(string name, COLUMN_TYPES colType, int intColStart, int intColLength)
 {
     this.strColName   = name;
     this.intColStart  = intColStart;
     this.intColLength = intColLength;
 }
Example #10
0
        // 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;
        }
Example #11
0
        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);
        }
Example #12
0
        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.
        }