public override int EstimateSize()
 {
     return(ByteUtil.EstimateSizeUtf8(value) + 6);
 }
 public override int EstimateSize()
 {
     // type + len + flags + ncells + jsonstr
     return(2 + 4 + 1 + 2 + ByteUtil.EstimateSizeUtf8(this.value));
 }
Example #3
0
        public void SetScan(ScanPolicy policy, string ns, string setName, string[] binNames, ulong taskId)
        {
            Begin();
            int fieldCount = 0;

            if (ns != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(ns) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (setName != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(setName) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (policy.recordsPerSecond > 0)
            {
                dataOffset += 4 + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            int predSize = 0;

            if (policy.predExp != null)
            {
                predSize = EstimatePredExp(policy.predExp);
                fieldCount++;
            }

            // Estimate scan options size.
            dataOffset += 2 + FIELD_HEADER_SIZE;
            fieldCount++;

            // Estimate scan timeout size.
            dataOffset += 4 + FIELD_HEADER_SIZE;
            fieldCount++;

            // Estimate taskId size.
            dataOffset += 8 + FIELD_HEADER_SIZE;
            fieldCount++;

            if (binNames != null)
            {
                foreach (string binName in binNames)
                {
                    EstimateOperationSize(binName);
                }
            }

            SizeBuffer();
            byte readAttr = (byte)Command.INFO1_READ;

            if (!policy.includeBinData)
            {
                readAttr |= (byte)Command.INFO1_NOBINDATA;
            }

            int operationCount = (binNames == null) ? 0 : binNames.Length;

            WriteHeader(policy, readAttr, 0, fieldCount, operationCount);

            if (ns != null)
            {
                WriteField(ns, FieldType.NAMESPACE);
            }

            if (setName != null)
            {
                WriteField(setName, FieldType.TABLE);
            }

            if (policy.recordsPerSecond > 0)
            {
                WriteField(policy.recordsPerSecond, FieldType.RECORDS_PER_SECOND);
            }

            if (policy.predExp != null)
            {
                WritePredExp(policy.predExp, predSize);
            }

            WriteFieldHeader(2, FieldType.SCAN_OPTIONS);
            byte priority = (byte)policy.priority;

            priority <<= 4;

            if (policy.failOnClusterChange)
            {
                priority |= 0x08;
            }

            dataBuffer[dataOffset++] = priority;
            dataBuffer[dataOffset++] = (byte)policy.scanPercent;

            // Write scan timeout
            WriteField(policy.socketTimeout, FieldType.SCAN_TIMEOUT);

            // Write taskId field
            WriteField(taskId, FieldType.TRAN_ID);

            if (binNames != null)
            {
                foreach (string binName in binNames)
                {
                    WriteOperation(binName, Operation.Type.READ);
                }
            }
            End();
        }
Example #4
0
        protected internal void SetQuery(Policy policy, Statement statement, bool write)
        {
            byte[] functionArgBuffer = null;
            int    fieldCount        = 0;
            int    filterSize        = 0;
            int    binNameSize       = 0;

            Begin();

            if (statement.ns != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.ns) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (statement.indexName != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.indexName) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (statement.setName != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.setName) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            // Allocate space for TaskId field.
            dataOffset += 8 + FIELD_HEADER_SIZE;
            fieldCount++;

            if (statement.filter != null)
            {
                IndexCollectionType type = statement.filter.CollectionType;

                if (type != IndexCollectionType.DEFAULT)
                {
                    dataOffset += FIELD_HEADER_SIZE + 1;
                    fieldCount++;
                }

                dataOffset += FIELD_HEADER_SIZE;
                filterSize++;                 // num filters
                filterSize += statement.filter.EstimateSize();
                dataOffset += filterSize;
                fieldCount++;

                // Query bin names are specified as a field (Scan bin names are specified later as operations)
                if (statement.binNames != null && statement.binNames.Length > 0)
                {
                    dataOffset += FIELD_HEADER_SIZE;
                    binNameSize++;                     // num bin names

                    foreach (string binName in statement.binNames)
                    {
                        binNameSize += ByteUtil.EstimateSizeUtf8(binName) + 1;
                    }
                    dataOffset += binNameSize;
                    fieldCount++;
                }
            }
            else
            {
                // Calling query with no filters is more efficiently handled by a primary index scan.
                // Estimate scan options size.
                dataOffset += 2 + FIELD_HEADER_SIZE;
                fieldCount++;

                // Estimate scan timeout size.
                dataOffset += 4 + FIELD_HEADER_SIZE;
                fieldCount++;

                // Estimate records per second size.
                if (statement.recordsPerSecond > 0)
                {
                    dataOffset += 4 + FIELD_HEADER_SIZE;
                    fieldCount++;
                }
            }

            PredExp[] predExp  = statement.PredExp;
            int       predSize = 0;

            if (policy.predExp != null && predExp == null)
            {
                predExp = policy.predExp;
            }

            if (predExp != null)
            {
                predSize = EstimatePredExp(predExp);
                fieldCount++;
            }

            if (statement.functionName != null)
            {
                dataOffset += FIELD_HEADER_SIZE + 1;                 // udf type
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.packageName) + FIELD_HEADER_SIZE;
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.functionName) + FIELD_HEADER_SIZE;

                if (statement.functionArgs.Length > 0)
                {
                    functionArgBuffer = Packer.Pack(statement.functionArgs);
                }
                else
                {
                    functionArgBuffer = new byte[0];
                }
                dataOffset += FIELD_HEADER_SIZE + functionArgBuffer.Length;
                fieldCount += 4;
            }

            // Operations (used in query execute) and bin names (used in scan/query) are mutually exclusive.
            int operationCount = 0;

            if (statement.operations != null)
            {
                foreach (Operation operation in statement.operations)
                {
                    EstimateOperationSize(operation);
                }
                operationCount = statement.operations.Length;
            }
            else if (statement.binNames != null && statement.filter == null)
            {
                foreach (string binName in statement.binNames)
                {
                    EstimateOperationSize(binName);
                }
                operationCount = statement.binNames.Length;
            }

            SizeBuffer();

            if (write)
            {
                WriteHeader((WritePolicy)policy, 0, Command.INFO2_WRITE, fieldCount, operationCount);
            }
            else
            {
                QueryPolicy qp       = (QueryPolicy)policy;
                int         readAttr = qp.includeBinData ? Command.INFO1_READ : Command.INFO1_READ | Command.INFO1_NOBINDATA;
                WriteHeader(policy, readAttr, 0, fieldCount, operationCount);
            }

            if (statement.ns != null)
            {
                WriteField(statement.ns, FieldType.NAMESPACE);
            }

            if (statement.indexName != null)
            {
                WriteField(statement.indexName, FieldType.INDEX_NAME);
            }

            if (statement.setName != null)
            {
                WriteField(statement.setName, FieldType.TABLE);
            }

            // Write taskId field
            WriteField(statement.taskId, FieldType.TRAN_ID);

            if (statement.filter != null)
            {
                IndexCollectionType type = statement.filter.CollectionType;

                if (type != IndexCollectionType.DEFAULT)
                {
                    WriteFieldHeader(1, FieldType.INDEX_TYPE);
                    dataBuffer[dataOffset++] = (byte)type;
                }

                WriteFieldHeader(filterSize, FieldType.INDEX_RANGE);
                dataBuffer[dataOffset++] = (byte)1;
                dataOffset = statement.filter.Write(dataBuffer, dataOffset);

                // Query bin names are specified as a field (Scan bin names are specified later as operations)
                if (statement.binNames != null && statement.binNames.Length > 0)
                {
                    WriteFieldHeader(binNameSize, FieldType.QUERY_BINLIST);
                    dataBuffer[dataOffset++] = (byte)statement.binNames.Length;

                    foreach (string binName in statement.binNames)
                    {
                        int len = ByteUtil.StringToUtf8(binName, dataBuffer, dataOffset + 1);
                        dataBuffer[dataOffset] = (byte)len;
                        dataOffset            += len + 1;
                    }
                }
            }
            else
            {
                // Calling query with no filters is more efficiently handled by a primary index scan.
                WriteFieldHeader(2, FieldType.SCAN_OPTIONS);
                byte priority = (byte)policy.priority;
                priority <<= 4;

                if (!write && ((QueryPolicy)policy).failOnClusterChange)
                {
                    priority |= 0x08;
                }

                dataBuffer[dataOffset++] = priority;
                dataBuffer[dataOffset++] = (byte)100;

                // Write scan socket idle timeout.
                WriteField(policy.socketTimeout, FieldType.SCAN_TIMEOUT);

                // Write records per second.
                if (statement.recordsPerSecond > 0)
                {
                    WriteField(statement.recordsPerSecond, FieldType.RECORDS_PER_SECOND);
                }
            }

            if (predExp != null)
            {
                WritePredExp(predExp, predSize);
            }

            if (statement.functionName != null)
            {
                WriteFieldHeader(1, FieldType.UDF_OP);
                dataBuffer[dataOffset++] = (statement.returnData) ? (byte)1 : (byte)2;
                WriteField(statement.packageName, FieldType.UDF_PACKAGE_NAME);
                WriteField(statement.functionName, FieldType.UDF_FUNCTION);
                WriteField(functionArgBuffer, FieldType.UDF_ARGLIST);
            }

            if (statement.operations != null)
            {
                foreach (Operation operation in statement.operations)
                {
                    WriteOperation(operation);
                }
            }
            else if (statement.binNames != null && statement.filter == null)
            {
                // Scan bin names are specified after all fields.
                foreach (string binName in statement.binNames)
                {
                    WriteOperation(binName, Operation.Type.READ);
                }
            }
            End();
        }
Example #5
0
        public void SetBatchRead(BatchPolicy policy, List <BatchRead> records, BatchNode batch)
        {
            // Estimate full row size
            int[]     offsets       = batch.offsets;
            int       max           = batch.offsetsSize;
            ushort    fieldCountRow = policy.sendSetName ? (ushort)2 : (ushort)1;
            BatchRead prev          = null;

            Begin();
            int fieldCount = 1;
            int predSize   = 0;

            if (policy.predExp != null)
            {
                predSize = EstimatePredExp(policy.predExp);
                fieldCount++;
            }

            dataOffset += FIELD_HEADER_SIZE + 5;

            for (int i = 0; i < max; i++)
            {
                BatchRead record   = records[offsets[i]];
                Key       key      = record.key;
                string[]  binNames = record.binNames;

                dataOffset += key.digest.Length + 4;

                // Avoid relatively expensive full equality checks for performance reasons.
                // Use reference equality only in hope that common namespaces/bin names are set from
                // fixed variables.  It's fine if equality not determined correctly because it just
                // results in more space used. The batch will still be correct.
                if (prev != null && prev.key.ns == key.ns &&
                    (!policy.sendSetName || prev.key.setName == key.setName) &&
                    prev.binNames == binNames && prev.readAllBins == record.readAllBins)
                {
                    // Can set repeat previous namespace/bin names to save space.
                    dataOffset++;
                }
                else
                {
                    // Estimate full header, namespace and bin names.
                    dataOffset += ByteUtil.EstimateSizeUtf8(key.ns) + FIELD_HEADER_SIZE + 6;

                    if (policy.sendSetName)
                    {
                        dataOffset += ByteUtil.EstimateSizeUtf8(key.setName) + FIELD_HEADER_SIZE;
                    }

                    if (binNames != null)
                    {
                        foreach (string binName in binNames)
                        {
                            EstimateOperationSize(binName);
                        }
                    }
                    prev = record;
                }
            }
            SizeBuffer();

            int readAttr = Command.INFO1_READ;

            if (policy.readModeAP == ReadModeAP.ALL)
            {
                readAttr |= Command.INFO1_READ_MODE_AP_ALL;
            }

            WriteHeader(policy, readAttr | Command.INFO1_BATCH, 0, fieldCount, 0);

            if (policy.predExp != null)
            {
                WritePredExp(policy.predExp, predSize);
            }

            int fieldSizeOffset = dataOffset;

            WriteFieldHeader(0, policy.sendSetName ? FieldType.BATCH_INDEX_WITH_SET : FieldType.BATCH_INDEX);             // Need to update size at end

            ByteUtil.IntToBytes((uint)max, dataBuffer, dataOffset);
            dataOffset += 4;
            dataBuffer[dataOffset++] = (policy.allowInline) ? (byte)1 : (byte)0;
            prev = null;

            for (int i = 0; i < max; i++)
            {
                int index = offsets[i];
                ByteUtil.IntToBytes((uint)index, dataBuffer, dataOffset);
                dataOffset += 4;

                BatchRead record   = records[index];
                Key       key      = record.key;
                string[]  binNames = record.binNames;
                byte[]    digest   = key.digest;
                Array.Copy(digest, 0, dataBuffer, dataOffset, digest.Length);
                dataOffset += digest.Length;

                // Avoid relatively expensive full equality checks for performance reasons.
                // Use reference equality only in hope that common namespaces/bin names are set from
                // fixed variables.  It's fine if equality not determined correctly because it just
                // results in more space used. The batch will still be correct.
                if (prev != null && prev.key.ns == key.ns &&
                    (!policy.sendSetName || prev.key.setName == key.setName) &&
                    prev.binNames == binNames && prev.readAllBins == record.readAllBins)
                {
                    // Can set repeat previous namespace/bin names to save space.
                    dataBuffer[dataOffset++] = 1;                     // repeat
                }
                else
                {
                    // Write full header, namespace and bin names.
                    dataBuffer[dataOffset++] = 0;                     // do not repeat

                    if (binNames != null && binNames.Length != 0)
                    {
                        dataBuffer[dataOffset++] = (byte)readAttr;
                        dataOffset += ByteUtil.ShortToBytes(fieldCountRow, dataBuffer, dataOffset);
                        dataOffset += ByteUtil.ShortToBytes((ushort)binNames.Length, dataBuffer, dataOffset);
                        WriteField(key.ns, FieldType.NAMESPACE);

                        if (policy.sendSetName)
                        {
                            WriteField(key.setName, FieldType.TABLE);
                        }

                        foreach (string binName in binNames)
                        {
                            WriteOperation(binName, Operation.Type.READ);
                        }
                    }
                    else
                    {
                        dataBuffer[dataOffset++] = (byte)(readAttr | (record.readAllBins ? Command.INFO1_GET_ALL : Command.INFO1_NOBINDATA));
                        dataOffset += ByteUtil.ShortToBytes(fieldCountRow, dataBuffer, dataOffset);
                        dataOffset += ByteUtil.ShortToBytes(0, dataBuffer, dataOffset);
                        WriteField(key.ns, FieldType.NAMESPACE);

                        if (policy.sendSetName)
                        {
                            WriteField(key.setName, FieldType.TABLE);
                        }
                    }
                    prev = record;
                }
            }

            // Write real field size.
            ByteUtil.IntToBytes((uint)(dataOffset - MSG_TOTAL_HEADER_SIZE - 4), dataBuffer, fieldSizeOffset);
            End();
        }
Example #6
0
        public void SetBatchRead(BatchPolicy policy, Key[] keys, BatchNode batch, string[] binNames, int readAttr)
        {
            // Estimate full row size
            int[]  offsets       = batch.offsets;
            int    max           = batch.offsetsSize;
            ushort fieldCountRow = policy.sendSetName ? (ushort)2 : (ushort)1;

            // Calculate size of bin names.
            int binNameSize    = 0;
            int operationCount = 0;

            if (binNames != null)
            {
                foreach (string binName in binNames)
                {
                    binNameSize += ByteUtil.EstimateSizeUtf8(binName) + OPERATION_HEADER_SIZE;
                }
                operationCount = binNames.Length;
            }

            // Estimate buffer size.
            Begin();
            int fieldCount = 1;
            int predSize   = 0;

            if (policy.predExp != null)
            {
                predSize = EstimatePredExp(policy.predExp);
                fieldCount++;
            }
            dataOffset += FIELD_HEADER_SIZE + 5;

            Key prev = null;

            for (int i = 0; i < max; i++)
            {
                Key key = keys[offsets[i]];

                dataOffset += key.digest.Length + 4;

                // Try reference equality in hope that namespace for all keys is set from a fixed variable.
                if (prev != null && prev.ns == key.ns && (!policy.sendSetName || prev.setName == key.setName))
                {
                    // Can set repeat previous namespace/bin names to save space.
                    dataOffset++;
                }
                else
                {
                    // Estimate full header, namespace and bin names.
                    dataOffset += ByteUtil.EstimateSizeUtf8(key.ns) + FIELD_HEADER_SIZE + 6;

                    if (policy.sendSetName)
                    {
                        dataOffset += ByteUtil.EstimateSizeUtf8(key.setName) + FIELD_HEADER_SIZE;
                    }
                    dataOffset += binNameSize;
                    prev        = key;
                }
            }

            SizeBuffer();

            if (policy.readModeAP == ReadModeAP.ALL)
            {
                readAttr |= Command.INFO1_READ_MODE_AP_ALL;
            }

            WriteHeader(policy, readAttr | Command.INFO1_BATCH, 0, fieldCount, 0);

            if (policy.predExp != null)
            {
                WritePredExp(policy.predExp, predSize);
            }

            int fieldSizeOffset = dataOffset;

            WriteFieldHeader(0, policy.sendSetName ? FieldType.BATCH_INDEX_WITH_SET : FieldType.BATCH_INDEX);             // Need to update size at end

            ByteUtil.IntToBytes((uint)max, dataBuffer, dataOffset);
            dataOffset += 4;
            dataBuffer[dataOffset++] = (policy.allowInline) ? (byte)1 : (byte)0;
            prev = null;

            for (int i = 0; i < max; i++)
            {
                int index = offsets[i];
                ByteUtil.IntToBytes((uint)index, dataBuffer, dataOffset);
                dataOffset += 4;

                Key    key    = keys[index];
                byte[] digest = key.digest;
                Array.Copy(digest, 0, dataBuffer, dataOffset, digest.Length);
                dataOffset += digest.Length;

                // Try reference equality in hope that namespace for all keys is set from a fixed variable.
                if (prev != null && prev.ns == key.ns && (!policy.sendSetName || prev.setName == key.setName))
                {
                    // Can set repeat previous namespace/bin names to save space.
                    dataBuffer[dataOffset++] = 1;                     // repeat
                }
                else
                {
                    // Write full header, namespace and bin names.
                    dataBuffer[dataOffset++] = 0;                     // do not repeat
                    dataBuffer[dataOffset++] = (byte)readAttr;
                    dataOffset += ByteUtil.ShortToBytes(fieldCountRow, dataBuffer, dataOffset);
                    dataOffset += ByteUtil.ShortToBytes((ushort)operationCount, dataBuffer, dataOffset);
                    WriteField(key.ns, FieldType.NAMESPACE);

                    if (policy.sendSetName)
                    {
                        WriteField(key.setName, FieldType.TABLE);
                    }

                    if (binNames != null)
                    {
                        foreach (string binName in binNames)
                        {
                            WriteOperation(binName, Operation.Type.READ);
                        }
                    }
                    prev = key;
                }
            }

            // Write real field size.
            ByteUtil.IntToBytes((uint)(dataOffset - MSG_TOTAL_HEADER_SIZE - 4), dataBuffer, fieldSizeOffset);
            End();
        }
Example #7
0
 private void EstimateOperationSize(string binName)
 {
     dataOffset += ByteUtil.EstimateSizeUtf8(binName) + OPERATION_HEADER_SIZE;
 }
Example #8
0
 private void EstimateOperationSize(Operation operation)
 {
     dataOffset += ByteUtil.EstimateSizeUtf8(operation.binName) + OPERATION_HEADER_SIZE;
     dataOffset += operation.value.EstimateSize();
 }
Example #9
0
 private void EstimateOperationSize(Bin bin)
 {
     dataOffset += ByteUtil.EstimateSizeUtf8(bin.name) + OPERATION_HEADER_SIZE;
     dataOffset += bin.value.EstimateSize();
 }
Example #10
0
        protected internal void SetQuery(Policy policy, Statement statement, bool write)
        {
            byte[] functionArgBuffer = null;
            int    fieldCount        = 0;
            int    filterSize        = 0;
            int    binNameSize       = 0;

            Begin();

            if (statement.ns != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.ns) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (statement.indexName != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.indexName) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (statement.setName != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.setName) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            // Allocate space for TaskId field.
            dataOffset += 8 + FIELD_HEADER_SIZE;
            fieldCount++;

            if (statement.filters != null)
            {
                if (statement.filters.Length >= 1)
                {
                    IndexCollectionType type = statement.filters[0].CollectionType;

                    if (type != IndexCollectionType.DEFAULT)
                    {
                        dataOffset += FIELD_HEADER_SIZE + 1;
                        fieldCount++;
                    }
                }

                dataOffset += FIELD_HEADER_SIZE;
                filterSize++;                 // num filters

                foreach (Filter filter in statement.filters)
                {
                    filterSize += filter.EstimateSize();
                }
                dataOffset += filterSize;
                fieldCount++;

                // Query bin names are specified as a field (Scan bin names are specified later as operations)
                if (statement.binNames != null)
                {
                    dataOffset += FIELD_HEADER_SIZE;
                    binNameSize++;                     // num bin names

                    foreach (string binName in statement.binNames)
                    {
                        binNameSize += ByteUtil.EstimateSizeUtf8(binName) + 1;
                    }
                    dataOffset += binNameSize;
                    fieldCount++;
                }
            }
            else
            {
                // Calling query with no filters is more efficiently handled by a primary index scan.
                // Estimate scan options size.
                dataOffset += 2 + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (statement.functionName != null)
            {
                dataOffset += FIELD_HEADER_SIZE + 1;                 // udf type
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.packageName) + FIELD_HEADER_SIZE;
                dataOffset += ByteUtil.EstimateSizeUtf8(statement.functionName) + FIELD_HEADER_SIZE;

                if (statement.functionArgs.Length > 0)
                {
                    functionArgBuffer = Packer.Pack(statement.functionArgs);
                }
                else
                {
                    functionArgBuffer = new byte[0];
                }
                dataOffset += FIELD_HEADER_SIZE + functionArgBuffer.Length;
                fieldCount += 4;
            }

            if (statement.filters == null)
            {
                if (statement.binNames != null)
                {
                    foreach (string binName in statement.binNames)
                    {
                        EstimateOperationSize(binName);
                    }
                }
            }

            SizeBuffer();
            int operationCount = (statement.filters == null && statement.binNames != null) ? statement.binNames.Length : 0;

            if (write)
            {
                WriteHeader((WritePolicy)policy, Command.INFO1_READ, Command.INFO2_WRITE, fieldCount, operationCount);
            }
            else
            {
                WriteHeader(policy, Command.INFO1_READ, 0, fieldCount, operationCount);
            }

            if (statement.ns != null)
            {
                WriteField(statement.ns, FieldType.NAMESPACE);
            }

            if (statement.indexName != null)
            {
                WriteField(statement.indexName, FieldType.INDEX_NAME);
            }

            if (statement.setName != null)
            {
                WriteField(statement.setName, FieldType.TABLE);
            }

            // Write taskId field
            WriteFieldHeader(8, FieldType.TRAN_ID);
            ByteUtil.LongToBytes(statement.taskId, dataBuffer, dataOffset);
            dataOffset += 8;

            if (statement.filters != null)
            {
                if (statement.filters.Length >= 1)
                {
                    IndexCollectionType type = statement.filters[0].CollectionType;

                    if (type != IndexCollectionType.DEFAULT)
                    {
                        WriteFieldHeader(1, FieldType.INDEX_TYPE);
                        dataBuffer[dataOffset++] = (byte)type;
                    }
                }

                WriteFieldHeader(filterSize, FieldType.INDEX_RANGE);
                dataBuffer[dataOffset++] = (byte)statement.filters.Length;

                foreach (Filter filter in statement.filters)
                {
                    dataOffset = filter.Write(dataBuffer, dataOffset);
                }

                // Query bin names are specified as a field (Scan bin names are specified later as operations)
                if (statement.binNames != null)
                {
                    WriteFieldHeader(binNameSize, FieldType.QUERY_BINLIST);
                    dataBuffer[dataOffset++] = (byte)statement.binNames.Length;

                    foreach (string binName in statement.binNames)
                    {
                        int len = ByteUtil.StringToUtf8(binName, dataBuffer, dataOffset + 1);
                        dataBuffer[dataOffset] = (byte)len;
                        dataOffset            += len + 1;
                    }
                }
            }
            else
            {
                // Calling query with no filters is more efficiently handled by a primary index scan.
                WriteFieldHeader(2, FieldType.SCAN_OPTIONS);
                byte priority = (byte)policy.priority;
                priority <<= 4;
                dataBuffer[dataOffset++] = priority;
                dataBuffer[dataOffset++] = (byte)100;
            }

            if (statement.functionName != null)
            {
                WriteFieldHeader(1, FieldType.UDF_OP);
                dataBuffer[dataOffset++] = (statement.returnData) ? (byte)1 : (byte)2;
                WriteField(statement.packageName, FieldType.UDF_PACKAGE_NAME);
                WriteField(statement.functionName, FieldType.UDF_FUNCTION);
                WriteField(functionArgBuffer, FieldType.UDF_ARGLIST);
            }

            // Scan bin names are specified after all fields.
            if (statement.filters == null)
            {
                if (statement.binNames != null)
                {
                    foreach (string binName in statement.binNames)
                    {
                        WriteOperation(binName, Operation.Type.READ);
                    }
                }
            }
            End();
        }
        public void SetScan(ScanPolicy policy, string ns, string setName, string[] binNames, long taskId)
        {
            Begin();
            int fieldCount = 0;

            if (ns != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(ns) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            if (setName != null)
            {
                dataOffset += ByteUtil.EstimateSizeUtf8(setName) + FIELD_HEADER_SIZE;
                fieldCount++;
            }

            // Estimate scan options size.
            dataOffset += 2 + FIELD_HEADER_SIZE;
            fieldCount++;

            // Estimate taskId size.
            dataOffset += 8 + FIELD_HEADER_SIZE;
            fieldCount++;

            if (binNames != null)
            {
                foreach (String binName in binNames)
                {
                    EstimateOperationSize(binName);
                }
            }

            SizeBuffer();
            byte readAttr = (byte)Command.INFO1_READ;

            if (!policy.includeBinData)
            {
                readAttr |= (byte)Command.INFO1_NOBINDATA;
            }

            int operationCount = (binNames == null) ? 0 : binNames.Length;

            WriteHeader(policy, readAttr, 0, fieldCount, operationCount);

            if (ns != null)
            {
                WriteField(ns, FieldType.NAMESPACE);
            }

            if (setName != null)
            {
                WriteField(setName, FieldType.TABLE);
            }

            WriteFieldHeader(2, FieldType.SCAN_OPTIONS);
            byte priority = (byte)policy.priority;

            priority <<= 4;

            if (policy.failOnClusterChange)
            {
                priority |= 0x08;
            }
            dataBuffer[dataOffset++] = priority;
            dataBuffer[dataOffset++] = (byte)policy.scanPercent;

            // Write taskId field
            WriteFieldHeader(8, FieldType.TRAN_ID);
            ByteUtil.LongToBytes((ulong)taskId, dataBuffer, dataOffset);
            dataOffset += 8;

            if (binNames != null)
            {
                foreach (String binName in binNames)
                {
                    WriteOperation(binName, Operation.Type.READ);
                }
            }
            End();
        }