Merge remote-tracking branch 'refs/remotes/Microsoft/master'

This commit is contained in:
Jovan Popovic
2017-02-02 16:07:27 +01:00
1425 changed files with 522 additions and 58371 deletions
@@ -1,49 +0,0 @@
# use glob syntax.
syntax: glob
*.ser
*.class
*~
*.bak
#*.off
*.old
# eclipse conf file
.settings
.classpath
.project
.manager
.scala_dependencies
# idea
.idea
*.iml
#visual studio
.suo
# building
target
build
null
tmp*
temp*
dist
test-output
build.log
out
packages
# other scm
.svn
.CVS
.hg*
# switch to regexp syntax.
# syntax: regexp
# ^\.pc/
#SHITTY output not in target directory
build.log
#dropbox
.DS_Store
Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

@@ -1,125 +0,0 @@
# In-Memory & Columnar Store Code Snack
In this code snack, developers will experience the benefit of performing real-time operation analytics enabled by leveraging a memory optimized table in combination with a columnstore index. The Visual Studio project contains a load generator that will be used to simulate a write heavy workload. They will initially run the simulator against a disk based table with a clustered index (btree) and take note of the rows inserted per second, and will measure the performance of a provided analytics query while the system is under the heavy write load. They will then author the T-SQL to create the memory optimized table with a columnstore index, update the load generator to target the memory optimized table and observe the improved performance characteristics. This code snack targets SQL Server on Linux.
## Requirements
- Visual Studio 2015 with Update 3 (or later)
- [SQL Server Data Tools for Visual Studio 2015](https://msdn.microsoft.com/en-us/mt186501)
- [SQL Server on Linux](https://www.microsoft.com/en-us/sql-server/sql-server-on-linux) running in [Docker](https://docs.docker.com/engine/installation/#/on-macos-and-windows)
- Your SQL on Linux Server should have at least 4 GB of RAM
## Clone the provided project
Clone this repo on to your local machine.
The recommended path is C:\In-Memory and Columnar\
## Download the sample data
This project requires a sample set of data you will load into SQL Server.
Download the data from: [http://bit.ly/2envb8m](http://bit.ly/2envb8m)
## Copy the sample data to your SQL on Linux host
1. On your Docker host machine, first grab the container ID of your SQL Server on Linux container. The Container ID is the value present in the first column.
```
docker ps
```
2. Copy over the sample data from the host to the container running SQL on Linux by using docker cp as follows (replace ContainerID with the container ID your retrieved). Note that the path /var/opt/mssql will map to C: in any T-SQL scripts executed against this server.
```
docker cp datapoints.bcp [ContainerID]:/var/opt/mssql/data/datapoints.bcp
```
3. Verify that the copy was successful. By connecting to bash within the container. Connect to your container (substitute your container ID in the command below) and list out the files:
```
docker exec -t -i [ContainerID] /bin/bash
ls /var/opt/mssql/data -1 -s -h
```
4. You should see datapoints.bcp in the listing
```
-rw-r--r-- 1 root root 256 Nov 13 22:49 Entropy.bin
-rw-r--r-- 1 root root 14M Nov 13 22:50 MSDBData.mdf
-rw-r--r-- 1 root root 768K Nov 14 22:48 MSDBLog.ldf
-rw-r--r-- 1 root root 161M Nov 11 04:18 datapoints.bcp
-rw-r--r-- 1 root root 4.0M Nov 14 22:55 master.mdf
-rw-r--r-- 1 root root 768K Nov 14 23:07 mastlog.ldf
-rw-r--r-- 1 root root 8.0M Nov 14 23:00 model.mdf
-rw-r--r-- 1 root root 8.0M Nov 14 23:00 modellog.ldf
-rw-r--r-- 1 root root 264M Nov 14 23:07 tempdb.mdf
-rw-r--r-- 1 root root 8.0M Nov 14 23:07 templog.ldf
drwxr-xr-x 3 root root 4.0K Nov 14 23:01 xtp
```
5. You are all set to continue the lab in Visual Studio 2015.
## Create the database and tables
1. Open the SqlLoadgenerator solution using Visual Studio 2015.
2. From Solution Explorer, expand the SqlGenerator solution, then SQL Resources folder and open "Create Database.sql".
3. Adjust the file paths for the FILENAME attributes if you installed SQL Server to a different location.
4. Select the Execute button
5. In the Connect dialog, provide your server name, authentication mode, username and password (as appropriate).
6. Wait for the script to complete successfully.
7. Within Visual Studio, open "Create Table- Disk Based.sql"
8. Execute the script to create the DataPointsDiskBased table.
This table will be used to store simulated IoT device telemetry, using traditional disk based table as well as clustered and non-clustered indexes on the fields commonly used in both point queries and analytic queries.
9. Within Visual Studio, open "Create Table- In Memory.sql"
10. Execute the script to create the DataPointsInMem table.
This table will be used to store the same simulated IoT device telemetry, but this time using a memory optimized table as well as clustered column store index against all fields (which will support analytic queries) and non-clustered hash indexes on the id field (which will support point lookups common to transactional queries).
```
CREATE TABLE [DataPointsInMem] (
-- ID should be a Primary Key, fields with a b-tree or hash index
Id bigint IDENTITY NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 30000000),
[Value] decimal(18,5),
[TimestampUtc] datetime,
DeviceId int,
-- This table should have a columnar index
INDEX Transactions_CCI CLUSTERED COLUMNSTORE
) WITH (
-- This should be an in-memory table
MEMORY_OPTIMIZED = ON
);
-- In-memory tables should auto-elevate their transaction level to Snapshot
ALTER DATABASE CURRENT SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT=ON ;
```
## Load initial data
1. Within Visual Studio, open "Load Sample Data.sql"
2. Adjust the path to the DataPoint.bcp file so it matches the location of your BCP file within the SQL on Linux container (if necessary) and save the script. Recall the path /var/opt/mssql will map to C: in any T-SQL scripts executed against SQL Server on Linux.
3. Execute the script to load each table with 4 million rows worth of sample data. This will take some time to complete.
## Execute the sample analytics query
1. Within Visual Studio, open "SampleQueries - DiskBased.sql".
2. Execute the script to summarize the time series data stored in the disk based table.
3. When the script completes, observe that 334 rows were returned.Take note of how long the query took to execute. Query time is shown in the bottom right of the document window in Visual Studio.
![alt text][Disk Based Results]
[Disk Based Results]: Images/DiskBasedResults.png "Disk Based Results"
4. Now, execute the script to summarize the time series data stored in the memory-optimized table, in "SampleQueries - InMemory.sql".
When the script completes, observe that 334 rows were returned.Take note of how long the query took to execute.
You should notice that the performance of the query against the memory-optimized table runs between 2x-10x faster than the same query, running against the same data stored in a disk based table. Query time is shown in the bottom right of the document window in Visual Studio.
![alt text][In-Memory Results]
[In-Memory Results]: Images/InMemoryResults.png "In-Memory Results"
## Execute the queries under load
1. Within Visual Studio, Solution Explorer, expand the SqlLoadGenerator project and then open "App.config".
2. Locate the connection string with the name "SqlConnection" and modify it so it points to your instance of SQL Server on Linux.
3. Save the App.config.
4. From the Debug menu, select Start Without Debugging.
5. At the prompt, choose option 1 to target the disk based table.
You should see log entries when every 1000 rows are inserted.
Leave the console running (it should run for about 3 minutes) and return to Visual Studio.
6. Open "SampleQueries - DiskBased.sql".
7. Execute the script to summarize the time series data stored in the disk based table.
8. Observe that more than 334 rows were returned.Take note of how long the query took to execute.
9. Repeat the query a few times, waiting a few seconds in between queries to get a sense of how long the query takes, even as new rows are inserted by the load generator.
10. Close the console load generator.
11. Run the SqlLoadGenerator again.
This time at the prompt, choose option 2 to target the memory-optimized table.
You should see log entries when every 1000 rows are inserted.
12. Leave the console running (it should run for about 3 minutes) and return to Visual Studio.
13. Open "SampleQueries - In Memory.sql".
14. Execute the script to summarize the time series data stored in the disk based table.
15. Observe that more than 334 rows were returned.Take note of how long the query took to execute.
Repeat the query a few times, waiting a few seconds in between queries to get a sense of how long the query takes, even as new rows are inserted by the load generator.
16. Close the console load generator.
## Conclusion
You should observe that while neither query was affected by the heavy insert load, the query against the analytics query continued to run 2x-10x faster than the same query against the disk-based table.
@@ -1,15 +0,0 @@
-- For SQL Server 2016, create the Database with a master data file (mdf),
-- the log data file (ldf) and a separate filegroup to support memory-optimized tables.
CREATE DATABASE [Telemetry]
CONTAINMENT = NONE
ON PRIMARY
( NAME = N'Telemetry', FILENAME = N'C:\data\Telemetry.mdf' , SIZE = 128MB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB ),
FILEGROUP [TelemetryInMem] CONTAINS MEMORY_OPTIMIZED_DATA DEFAULT
( NAME = N'Telemetry_mem', FILENAME = N'C:\data\Telemetry_mem' , MAXSIZE = UNLIMITED)
LOG ON
( NAME = N'Telemetry_log', FILENAME = N'C:\data\Telemetry_log.ldf' , SIZE = 128MB , MAXSIZE = 2048GB , FILEGROWTH = 65536KB )
GO
ALTER DATABASE [Telemetry] SET COMPATIBILITY_LEVEL = 130
GO
@@ -1,24 +0,0 @@
USE Telemetry;
GO
DROP INDEX IF EXISTS dbo.DataPointsDiskBased.IX_DeviceId;
DROP INDEX IF EXISTS dbo.DataPointsDiskBased.IX_Timestamp;
DROP TABLE IF EXISTS dbo.DataPointsDiskBased;
CREATE TABLE [DataPointsDiskBased] (
Id bigint IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
[Value] decimal(18,5),
[TimestampUtc] datetime,
DeviceId int,
);
CREATE NONCLUSTERED INDEX IX_DeviceId
ON dbo.DataPointsDiskBased (DeviceId);
GO
CREATE NONCLUSTERED INDEX IX_Timestamp
ON dbo.DataPointsDiskBased (TimestampUtc);
GO
@@ -1,22 +0,0 @@
USE Telemetry;
GO
DROP INDEX IF EXISTS dbo.DataPointsInMem.IX_DeviceId;
DROP INDEX IF EXISTS dbo.DataPointsInMem.IX_Timestamp;
DROP TABLE IF EXISTS dbo.DataPointsInMem;
CREATE TABLE [DataPointsInMem] (
-- ID should be a Primary Key, fields with a b-tree or hash index
Id bigint IDENTITY NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 30000000),
[Value] decimal(18,5),
[TimestampUtc] datetime,
DeviceId int,
-- This table should have a columnar index
INDEX Transactions_CCI CLUSTERED COLUMNSTORE
) WITH (
-- This should be an in-memory table
MEMORY_OPTIMIZED = ON
);
-- In-memory tables should auto-elevate their transaction level to Snapshot
ALTER DATABASE CURRENT SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT=ON ;
@@ -1,17 +0,0 @@
USE Telemetry;
GO
BULK INSERT Telemetry.dbo.DataPointsDiskBased
FROM 'C:\data\datapoints.bcp'
WITH (
DATAFILETYPE = 'native'
);
GO
BULK INSERT Telemetry.dbo.DataPointsInMem
FROM 'C:\data\datapoints.bcp'
WITH (
DATAFILETYPE = 'native'
);
GO
@@ -1,21 +0,0 @@
USE Telemetry;
SELECT
Count(*) Counted,
Max([Value]) MaxValue,
Avg([Value]) AvgValue,
Min([Value]) MinValue,
DatePart(YYYY, TimestampUtc) [year],
DatePart(MM, TimestampUtc) [month],
DatePart(DD, TimestampUtc) [day],
DatePart(hh, TimestampUtc) [hour],
DatePart(mi, TimestampUtc) [minute],
DatePart(ss, TimestampUtc) [second]
FROM DataPointsDiskBased
GROUP BY
DatePart(YYYY, TimestampUtc),
DatePart(MM, TimestampUtc),
DatePart(DD, TimestampUtc),
DatePart(hh, TimestampUtc),
DatePart(mi, TimestampUtc),
DatePart(ss, TimestampUtc)
@@ -1,24 +0,0 @@
USE Telemetry;
SELECT
Count(*) Counted,
Max([Value]) MaxValue,
Avg([Value]) AvgValue,
Min([Value]) MinValue,
DatePart(YYYY, TimestampUtc) [year],
DatePart(MM, TimestampUtc) [month],
DatePart(DD, TimestampUtc) [day],
DatePart(hh, TimestampUtc) [hour],
DatePart(mi, TimestampUtc) [minute],
DatePart(ss, TimestampUtc) [second]
FROM DataPointsInMem
GROUP BY
DatePart(YYYY, TimestampUtc),
DatePart(MM, TimestampUtc),
DatePart(DD, TimestampUtc),
DatePart(hh, TimestampUtc),
DatePart(mi, TimestampUtc),
DatePart(ss, TimestampUtc)
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="SqlLoadGenerator.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<connectionStrings>
<add name="SqlConnection"
connectionString="data source=192.168.5.105;initial catalog=telemetry;user id=sa;password=Abc1234567890;MultipleActiveResultSets=True;App=LoadGenerator" providerName="System.Data.SqlClient" />
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<userSettings>
<SqlLoadGenerator.Properties.Settings>
<setting name="Spike_NumRowsToInsert" serializeAs="String">
<value>200000</value>
</setting>
<setting name="Spike_NumParallelClients" serializeAs="String">
<value>1</value>
</setting>
</SqlLoadGenerator.Properties.Settings>
</userSettings>
</configuration>
@@ -1,157 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Diagnostics;
namespace SqlLoadGenerator
{
class Program
{
static string _connectionString;
static int _numRowsToInsert = Properties.Settings.Default.Spike_NumRowsToInsert;
static int _numTaskPerSpike = Properties.Settings.Default.Spike_NumParallelClients;
static Stopwatch _stopwatch;
static string _tableName = "DataPointsDiskBased";
static void Main(string[] args)
{
Console.WriteLine("Which table would you like to target for load? (press 1 or 2)");
Console.WriteLine("1. Disk Based Table");
Console.WriteLine("2. Memory-Optimized Table");
if (Console.ReadKey().KeyChar == '1')
{
_tableName = "DataPointsDiskBased";
}
else
{
_tableName = "DataPointsInMem";
}
Console.WriteLine("");
_connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["SqlConnection"].ConnectionString;
_stopwatch = Stopwatch.StartNew();
List<Task> tasks = new List<Task>();
tasks.AddRange(ScheduleLoadSpike(_numTaskPerSpike));
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Tasks completed.");
_stopwatch.Stop();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("{0} hours {1} minutes {2} seconds elapsed.",
_stopwatch.Elapsed.Hours, _stopwatch.Elapsed.Minutes, _stopwatch.Elapsed.Seconds);
Console.ReadLine();
}
static Task[] ScheduleLoadSpike(int numTasks)
{
Task[] tasks = new Task[numTasks];
for (int i = 0; i < numTasks; i++)
{
tasks[i] = Task.Run(() => GenerateLoadSpike());
}
return tasks;
}
static int _numTasks = 0;
static int GenerateLoadSpike()
{
Random Rand = new Random();
int taskID = System.Threading.Interlocked.Increment(ref _numTasks);
Console.WriteLine("{0}: Preparing load spike...", taskID);
int numRowsAffected = 0;
try
{
SqlConnection conn = new SqlConnection(_connectionString);
string commandText = String.Format("INSERT [dbo].[{0}] ([Value], TimestampUtc, DeviceId) " +
"VALUES (@Value, @TimestampUtc, @DeviceId)", _tableName);
Random r = new Random(1);
conn.Open();
for (int i = 0; i < _numRowsToInsert; i++)
{
// if a transient error closed our connection, create a new one and open it
if (conn.State == System.Data.ConnectionState.Closed)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("{0}: Re-creating closed connection", taskID);
Console.ResetColor();
conn = new SqlConnection(_connectionString);
conn.Open();
}
double value = 9999 + 100 * Rand.NextDouble();
List<SqlParameter> parameters = new List<SqlParameter>() {
new SqlParameter("@Value", value),
new SqlParameter("@TimestampUtc", DateTime.UtcNow),
new SqlParameter("@DeviceId", taskID)
};
try
{
using (SqlCommand cmd = new SqlCommand(commandText, conn))
{
cmd.CommandType = System.Data.CommandType.Text;
cmd.Parameters.AddRange(parameters.ToArray());
numRowsAffected += cmd.ExecuteNonQuery();
}
}
catch (SqlException sqlex)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(sqlex.Message);
Console.ResetColor();
System.Threading.Thread.Sleep(200);
}
catch (Exception cmdex)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(cmdex.Message);
Console.ResetColor();
}
if (i % 1000 == 0)
{
Console.WriteLine("{0}: Inserted {1} new rows so far", taskID, numRowsAffected);
}
}
conn.Close();
conn.Dispose();
Console.WriteLine("{0}: Inserted {1} new rows", taskID, numRowsAffected);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.Message);
Console.ResetColor();
}
Console.WriteLine("{0}: Finished with load spike.", taskID);
return numRowsAffected;
}
}
}
@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SqlLoadGenerator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SqlLoadGenerator")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("59486e0e-6932-4191-94ff-04082db46472")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -1,50 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SqlLoadGenerator.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("6000000")]
public int Spike_NumRowsToInsert {
get {
return ((int)(this["Spike_NumRowsToInsert"]));
}
set {
this["Spike_NumRowsToInsert"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1")]
public int Spike_NumParallelClients {
get {
return ((int)(this["Spike_NumParallelClients"]));
}
set {
this["Spike_NumParallelClients"] = value;
}
}
}
}
@@ -1,12 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="SqlLoadGenerator.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="Spike_NumRowsToInsert" Type="System.Int32" Scope="User">
<Value Profile="(Default)">6000000</Value>
</Setting>
<Setting Name="Spike_NumParallelClients" Type="System.Int32" Scope="User">
<Value Profile="(Default)">1</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -1,70 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{59486E0E-6932-4191-94FF-04082DB46472}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SqlLoadGenerator</RootNamespace>
<AssemblyName>SqlLoadGenerator</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -1 +0,0 @@
Coming soon
Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

@@ -1,95 +0,0 @@
# In-Memory & Columnar Store Code Snack (SQL Server 2016)
In this code snack, developers will experience the benefit of performing real-time operation analytics enabled by leveraging a memory optimized table in combination with a columnstore index. The Visual Studio project contains a load generator that will be used to simulate a write heavy workload. They will initially run the simulator against a disk based table with a clustered index (btree) and take note of the rows inserted per second, and will measure the performance of a provided analytics query while the system is under the heavy write load. They will then author the T-SQL to create the memory optimized table with a columnstore index, update the load generator to target the memory optimized table and observe the improved performance characteristics.
## Requirements
- Visual Studio 2015 with Update 3 (or later)
- [SQL Server Data Tools for Visual Studio 2015](https://msdn.microsoft.com/en-us/mt186501)
- SQL Server 2016 Developer Edition (or higher)
- Your developer machine should have at least 8 GB of RAM
## Clone the provided project
Clone this repo on to your local machine.
The recommended path is C:\In-Memory and Columnar\
## Download the sample data
This project requires a sample set of data you will load into SQL Server.
Download the data from: [http://bit.ly/2envb8m](http://bit.ly/2envb8m)
## Create the database and tables
1. Open the SqlLoadgenerator solution using Visual Studio 2015.
2. From Solution Explorer, expand the SqlGenerator solution, then SQL Resources folder and open "Create Database.sql".
3. Adjust the file paths for the FILENAME attributes if you installed SQL Server to a different location.
4. Select the Execute button
5. In the Connect dialog, provide your server name, authentication mode, username and password (as appropriate).
6. Wait for the script to complete successfully.
7. Within Visual Studio, open "Create Table- Disk Based.sql"
8. Execute the script to create the DataPointsDiskBased table.
This table will be used to store simulated IoT device telemetry, using traditional disk based table as well as clustered and non-clustered indexes on the fields commonly used in both point queries and analytic queries.
9. Within Visual Studio, open "Create Table- In Memory.sql"
10. Execute the script to create the DataPointsInMem table.
This table will be used to store the same simulated IoT device telemetry, but this time using a memory optimized table as well as clustered column store index against all fields (which will support analytic queries) and non-clustered hash indexes on the id field (which will support point lookups common to transactional queries).
```
CREATE TABLE [DataPointsInMem] (
-- ID should be a Primary Key, fields with a b-tree or hash index
Id bigint IDENTITY NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 30000000),
[Value] decimal(18,5),
[TimestampUtc] datetime,
DeviceId int,
-- This table should have a columnar index
INDEX Transactions_CCI CLUSTERED COLUMNSTORE
) WITH (
-- This should be an in-memory table
MEMORY_OPTIMIZED = ON
);
-- In-memory tables should auto-elevate their transaction level to Snapshot
ALTER DATABASE CURRENT SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT=ON ;
```
## Load initial data
1. Within Visual Studio, open "Load Sample Data.sql"
2. Adjust the path to the DataPoint.bcp file so it matches the location of your project and save the script.
3. Execute the script to load each table with 4 million rows worth of sample data. This will take some time to complete.
## Execute the sample analytics query
1. Within Visual Studio, open "SampleQueries - DiskBased.sql".
2. Execute the script to summarize the time series data stored in the disk based table.
3. When the script completes, observe that 334 rows were returned.Take note of how long the query took to execute. Query time is shown in the bottom right of the document window in Visual Studio.
![alt text][Disk Based Results]
[Disk Based Results]: Images/DiskBasedResults.png "Disk Based Results"
4. Now, execute the script to summarize the time series data stored in the memory-optimized table, in "SampleQueries - InMemory.sql".
When the script completes, observe that 334 rows were returned.Take note of how long the query took to execute.
You should notice that the performance of the query against the memory-optimized table runs between 2x-10x faster than the same query, running against the same data stored in a disk based table. Query time is shown in the bottom right of the document window in Visual Studio.
![alt text][In-Memory Results]
[In-Memory Results]: Images/InMemoryResults.png "In-Memory Results"
## Execute the queries under load
1. Within Visual Studio, Solution Explorer, expand the SqlLoadGenerator project and then open "App.config".
2. Locate the connection string with the name "SqlConnection" and modify it so it points to your instance of SQL Server 2016.
3. Save the App.config.
4. From the Debug menu, select Start Without Debugging.
5. At the prompt, choose option 1 to target the disk based table.
You should see log entries when every 1000 rows are inserted.
Leave the console running (it should run for about 3 minutes) and return to Visual Studio.
6. Open "SampleQueries - DiskBased.sql".
7. Execute the script to summarize the time series data stored in the disk based table.
8. Observe that more than 334 rows were returned.Take note of how long the query took to execute.
9. Repeat the query a few times, waiting a few seconds in between queries to get a sense of how long the query takes, even as new rows are inserted by the load generator.
10. Close the console load generator.
11. Run the SqlLoadGenerator again.
This time at the prompt, choose option 2 to target the memory-optimized table.
You should see log entries when every 1000 rows are inserted.
12. Leave the console running (it should run for about 3 minutes) and return to Visual Studio.
13. Open "SampleQueries - In Memory.sql".
14. Execute the script to summarize the time series data stored in the disk based table.
15. Observe that more than 334 rows were returned.Take note of how long the query took to execute.
Repeat the query a few times, waiting a few seconds in between queries to get a sense of how long the query takes, even as new rows are inserted by the load generator.
16. Close the console load generator.
## Conclusion
You should observe that while neither query was affected by the heavy insert load, the query against the analytics query continued to run 2x-10x faster than the same query against the disk-based table.
@@ -0,0 +1,86 @@
# Define the connection string
connStr <- paste("Driver=SQL Server;Server=", "MyServer", ";Database=", "tpcx1b", ";Trusted_Connection=true;", sep = "");
# Input Query
input_query <- "
SELECT
ss_customer_sk AS customer,
round(CASE WHEN ((orders_count = 0) OR (returns_count IS NULL) OR (orders_count IS NULL) OR ((returns_count / orders_count) IS NULL) ) THEN 0.0 ELSE (cast(returns_count as nchar(10)) / orders_count) END, 7) AS orderRatio,
round(CASE WHEN ((orders_items = 0) OR(returns_items IS NULL) OR (orders_items IS NULL) OR ((returns_items / orders_items) IS NULL) ) THEN 0.0 ELSE (cast(returns_items as nchar(10)) / orders_items) END, 7) AS itemsRatio,
round(CASE WHEN ((orders_money = 0) OR (returns_money IS NULL) OR (orders_money IS NULL) OR ((returns_money / orders_money) IS NULL) ) THEN 0.0 ELSE (cast(returns_money as nchar(10)) / orders_money) END, 7) AS monetaryRatio,
round(CASE WHEN ( returns_count IS NULL ) THEN 0.0 ELSE returns_count END, 0) AS frequency
FROM
(
SELECT
ss_customer_sk,
-- return order ratio
COUNT(distinct(ss_ticket_number)) AS orders_count,
-- return ss_item_sk ratio
COUNT(ss_item_sk) AS orders_items,
-- return monetary amount ratio
SUM( ss_net_paid ) AS orders_money
FROM store_sales s
GROUP BY ss_customer_sk
) orders
LEFT OUTER JOIN
(
SELECT
sr_customer_sk,
-- return order ratio
count(distinct(sr_ticket_number)) as returns_count,
-- return ss_item_sk ratio
COUNT(sr_item_sk) as returns_items,
-- return monetary amount ratio
SUM( sr_return_amt ) AS returns_money
FROM store_returns
GROUP BY sr_customer_sk
) returned ON ss_customer_sk=sr_customer_sk
"
# Input customer data that needs to be classified
customer_returns <- RxSqlServerData(sqlQuery = input_query,
colClasses = c(customer = "numeric", orderRatio = "numeric", itemsRatio = "numeric", monetaryRatio = "numeric", frequency = "numeric"),
connectionString = connStr);
# Transform the data from an input dataset to an output dataset
customer_data <- rxDataStep(customer_returns);
#Look at the data we just loaded from SQL Server
head(customer_data, n = 5);
# Determine number of clusters
#Using a plot of the within groups sum of squares by number of clusters extracted can help determine the appropriate number of clusters.
#We are looking for a bend in the plot. It is at this "elbow" in the plot that we have the appropriate number of clusters
wss <- (nrow(customer_data) - 1) * sum(apply(customer_data, 2, var))
for (i in 2:20) {
xt = kmeans(customer_data, centers = i)
wss[i] <- sum(kms = kmeans(customer_data, centers = i)$withinss)
}
plot(1:20, wss, type = "b", xlab = "Number of Clusters", ylab = "Within groups sum of squares")
# Output table to hold the customer group mappings
return_cluster = RxSqlServerData(table = "return_cluster", connectionString = connStr);
# Set.seed for random number generator for predictability
set.seed(10);
# Generate clusters using rxKmeans and output key / cluster to a table in SQL Server called return_cluster
clust <- rxKmeans( ~ orderRatio + itemsRatio + monetaryRatio + frequency, customer_returns, numClusters = 4
, outFile = return_cluster, outColName = "cluster", extraVarsToWrite = c("customer"), overwrite = TRUE);
# Read the custome returns cluster table
customer_cluster <- rxDataStep(return_cluster);
#Plot the clusters (need to install library "cluster")
#install.packages("cluster")
library("cluster");
clusplot(customer_data, customer_cluster$cluster, color=TRUE, shade=TRUE, labels=4, lines=0, plotchar = TRUE);
#Look at the clustering details and analyze results
clust
@@ -0,0 +1,101 @@
USE [tpcxbb_1gb]
DROP PROC IF EXISTS generate_customer_return_clusters;
GO
CREATE procedure [dbo].[generate_customer_return_clusters]
AS
/*
This procedure uses R to classify customers into different groups based on their
purchase & return history.
*/
BEGIN
DECLARE @duration FLOAT
, @instance_name NVARCHAR(100) = @@SERVERNAME
, @database_name NVARCHAR(128) = db_name()
-- Input query to generate the purchase history & return metrics
, @input_query NVARCHAR(MAX) = N'
SELECT
ss_customer_sk AS customer,
round(CASE WHEN ((orders_count = 0) OR (returns_count IS NULL) OR (orders_count IS NULL) OR ((returns_count / orders_count) IS NULL) ) THEN 0.0 ELSE (cast(returns_count as nchar(10)) / orders_count) END, 7) AS orderRatio,
round(CASE WHEN ((orders_items = 0) OR(returns_items IS NULL) OR (orders_items IS NULL) OR ((returns_items / orders_items) IS NULL) ) THEN 0.0 ELSE (cast(returns_items as nchar(10)) / orders_items) END, 7) AS itemsRatio,
round(CASE WHEN ((orders_money = 0) OR (returns_money IS NULL) OR (orders_money IS NULL) OR ((returns_money / orders_money) IS NULL) ) THEN 0.0 ELSE (cast(returns_money as nchar(10)) / orders_money) END, 7) AS monetaryRatio,
round(CASE WHEN ( returns_count IS NULL ) THEN 0.0 ELSE returns_count END, 0) AS frequency
FROM
(
SELECT
ss_customer_sk,
-- return order ratio
COUNT(distinct(ss_ticket_number)) AS orders_count,
-- return ss_item_sk ratio
COUNT(ss_item_sk) AS orders_items,
-- return monetary amount ratio
SUM( ss_net_paid ) AS orders_money
FROM store_sales s
GROUP BY ss_customer_sk
) orders
LEFT OUTER JOIN
(
SELECT
sr_customer_sk,
-- return order ratio
count(distinct(sr_ticket_number)) as returns_count,
-- return ss_item_sk ratio
COUNT(sr_item_sk) as returns_items,
-- return monetary amount ratio
SUM( sr_return_amt ) AS returns_money
FROM store_returns
GROUP BY sr_customer_sk
) returned ON ss_customer_sk=sr_customer_sk
'
EXEC sp_execute_external_script
@language = N'R'
, @script = N'
# Define the connection string
connStr <- paste("Driver=SQL Server;Server=", instance_name, ";Database=", database_name, ";Trusted_Connection=true;", sep="");
# Input customer data that needs to be classified. This is the result we get from our query
customer_returns <- RxSqlServerData(sqlQuery = input_query,
colClasses = c(customer = "numeric", orderRatio = "numeric", itemsRatio = "numeric", monetaryRatio = "numeric", frequency = "numeric"),
connectionString = connStr);
# Output table to hold the customer cluster mappings
return_cluster = RxSqlServerData(table = "customer_return_clusters", connectionString = connStr);
# set.seed for random number generator for predictability
set.seed(10);
# generate clusters using rxKmeans and output clusters to a table called "customer_return_clusters".
clust <- rxKmeans( ~ orderRatio + itemsRatio + monetaryRatio + frequency, customer_returns, numClusters = 4
, outFile = return_cluster, outColName = "cluster", writeModelVars = TRUE , extraVarsToWrite = c("customer"), overwrite = TRUE);
'
, @input_data_1 = N''
, @params = N'@instance_name nvarchar(100), @database_name nvarchar(128), @input_query nvarchar(max), @duration float OUTPUT'
, @instance_name = @instance_name
, @database_name = @database_name
, @input_query = @input_query
, @duration = @duration OUTPUT;
END;
GO
--Empty table of the results before running the stored procedure
TRUNCATE TABLE customer_return_clusters;
--Execute the clustering. This will load the table customer_return_clusters with cluster mappings
EXEC [dbo].[generate_customer_return_clusters];
--Now select data from table customer_return_clusters to verify that the clustering data was loaded
SELECT * FROM customer_return_clusters;
--Select email addresses of customers in cluster 1
SELECT customer.[c_email_address], customer.c_customer_sk
FROM dbo.customer
JOIN
[dbo].[customer_return_clusters] as r
ON r.customer = customer.c_customer_sk
WHERE r.cluster = 3
@@ -0,0 +1,70 @@
# Perform customer clustering with SQL Server R Services
In this sample, we are going to get ourselves familiar with clustering.
Clustering can be explained as organizing data into groups where members of a group are similar in some way.
### Contents
[About this sample](#about-this-sample)<br/>
[Before you begin](#before-you-begin)<br/>
[Sample details](#sample-details)<br/>
[Related links](#related-links)<br/>
<a name=about-this-sample></a>
## About this sample
We will be using the Kmeans algorithm to perform the clustering of customers. This can for example be used to target a specific group of customers for marketing efforts.
Kmeans clustering is an unsupervised learning algorithm that tries to group data based on similarities. Unsupervised learning means that there is no outcome to be predicted, and the algorithm just tries to find patterns in the data.
In this sample, you will learn how to perform Kmeans clustering in R and deploying the solution in SQL Server 2016.
Follow the step by step tutorial [here](https://www.microsoft.com/en-us/sql-server/developer-get-started/rclustering) to walk through this sample.
<!-- Delete the ones that don't apply -->
- **Applies to:** SQL Server 2016 (or higher)
- **Key features:**
- **Workload:** SQL Server R Services
- **Programming Language:** T-SQL, R
- **Authors:** Nellie Gustafsson
- **Update history:** Getting started tutorial for R Services
<a name=before-you-begin></a>
## Before you begin
To run this sample, you need the following prerequisites.
Section 1 in the [tutorial](https://www.microsoft.com/en-us/sql-server/developer-get-started/rclustering) covers the prerequisites.
After that, you can download a DB backup file and restore it using Setup.sql. [Download DB](https://sqlchoice.blob.core.windows.net/sqlchoice/static/tpcxbb_1gb.bak)
**Software prerequisites:**
<!-- Examples -->
1. SQL Server 2016 (or higher) with R Services installed
2. SQL Server Management Studio
3. R IDE Tool like Visual Studio
<a name=sample-details></a>
## Sample Details
### Customer Clustering.R
The R script that performs clustering.
### Customer Clustering.SQL
The SQL code to create stored procedure that performs clustering, and queries to verify and take further actions.
<a name=related-links></a>
## Related Links
<!-- Links to more articles. Remember to delete "en-us" from the link path. -->
For additional content, see these articles:
[SQL Server R Services - Upgrade and Installation FAQ](https://msdn.microsoft.com/en-us/library/mt653951.aspx)
[Other SQL Server R Services Tutorials](https://msdn.microsoft.com/en-us/library/mt591993.aspx)
@@ -0,0 +1,13 @@
-- Before we start, we need to restore the DB for this tutorial.
-- Step1: Download the compressed backup file
-- Save the file on a location where SQL Server can access it. For example: C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\
-- In a new query window in SSMS, execute the following restore statement, but REMEMBER TO CHANGE THE FILE PATHS
-- to match the directories of your installation!
USE master;
GO
RESTORE DATABASE tpcxbb_1gb
FROM DISK = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\tpcxbb_1gb.bak'
WITH
MOVE 'tpcxbb_1gb' TO 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\tpcxbb_1gb.mdf'
,MOVE 'tpcxbb_1gb_log' TO 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\tpcxbb_1gb.ldf';
GO
@@ -0,0 +1,54 @@
#Connection string to connect to SQL Server
connStr <- paste("Driver=SQL Server; Server=", "MyServer",
";Database=", "tutorialdb", ";Trusted_Connection=true;", sep = "");
#Get the data from SQL Server Table
SQL_rentaldata <- RxSqlServerData(table = "dbo.rental_data",
connectionString = connStr, returnDataFrame = TRUE);
#Import the data into a data frame
rentaldata <- rxImport(SQL_rentaldata);
#Let's see the structure of the data and the top rows
head(rentaldata);
str(rentaldata);
#Changing the three factor columns to factor types
#This helps when building the model because we are explicitly saying that these values are categorical
rentaldata$Holiday <- factor(rentaldata$Holiday);
rentaldata$Snow <- factor(rentaldata$Snow);
rentaldata$WeekDay <- factor(rentaldata$WeekDay);
#Visualize the dataset after the change
str(rentaldata);
#Now let's split the dataset into 2 different sets
#One set for training the model and the other for validating it
train_data = rentaldata[rentaldata$Year < 2015,];
test_data = rentaldata[rentaldata$Year == 2015,];
#Use this column to check the quality of the prediction against actual values
actual_counts <- test_data$RentalCount;
#Model 1: Use rxLinMod to create a linear regression model. We are training the data using the training data set
model_linmod <- rxLinMod(RentalCount ~ Month + Day + WeekDay + Snow + Holiday, data = train_data);
#Model 2: Use rxDTree to create a decision tree model. We are training the data using the training data set
model_dtree <- rxDTree(RentalCount ~ Month + Day + WeekDay + Snow + Holiday, data = train_data);
#Use the models we just created to predict using the test data set.
#That enables us to compare actual values of RentalCount from the two models and compare to the actual values in the test data set
predict_linmod <- rxPredict(model_linmod, test_data, writeModelVars = TRUE);
predict_dtree <- rxPredict(model_dtree, test_data, writeModelVars = TRUE);
#Look at the top rows of the two prediction data sets.
head(predict_linmod);
head(predict_dtree);
#Now we will use the plotting functionality in R to viusalize the results from the predictions
#We are plotting the difference between actual and predicted values for both models to compare accuracy
par(mfrow = c(2, 1));
plot(predict_linmod$RentalCount_Pred - predict_linmod$RentalCount, main = "Difference between actual and predicted. rxLinmod");
plot(predict_dtree$RentalCount_Pred - predict_dtree$RentalCount, main = "Difference between actual and predicted. rxDTree");
@@ -0,0 +1,103 @@
--Before we start, we need to restore the DB for this tutorial.
--Step1:Download the compressed backup file
--Save the file on a location where SQL Server can access it. For example:C:\Program Files \Microsoft SQL Server \MSSQL13.MSSQLSERVER\MSSQL\Backup\
--In a new query window in SSMS, execute the following restore statement, but REMEMBER TO CHANGE THE FILE PATHS
--to match the directories of your installation!
USE master;
GO
RESTORE DATABASE TutorialDB
FROM DISK = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\TutorialDB.bak'
WITH
MOVE 'TutorialDB' TO 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\TutorialDB.mdf'
, MOVE 'TutorialDB_log' TO 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\TutorialDB.ldf';
GO
USE tutorialdb;
SELECT * FROM [dbo].[rental_data];
-- Operationalize
USE tutorialdb;
GO
-- Setup model table
DROP TABLE IF EXISTS rental_rx_models;
GO
CREATE TABLE rental_rx_models (
model_name VARCHAR(30) NOT NULL DEFAULT('default model') PRIMARY KEY,
model VARBINARY(MAX) NOT NULL
);
GO
-- Stored procedure that trains and generates a model using the rental_data and a decision tree algorithm
DROP PROCEDURE IF EXISTS generate_rental_rx_model;
go
CREATE PROCEDURE generate_rental_rx_model (@trained_model varbinary(max) OUTPUT)
AS
BEGIN
EXECUTE sp_execute_external_script
@language = N'R'
, @script = N'
require("RevoScaleR");
rental_train_data$Holiday = factor(rental_train_data$Holiday);
rental_train_data$Snow = factor(rental_train_data$Snow);
rental_train_data$WeekDay = factor(rental_train_data$WeekDay);
#Create a dtree model and train it using the training data set
model_dtree <- rxDTree(RentalCount ~ Month + Day + WeekDay + Snow + Holiday, data = rental_train_data);
#Before saving the model to the DB table, we need to serialize it
trained_model <- as.raw(serialize(model_dtree, connection=NULL));'
, @input_data_1 = N'select "RentalCount", "Month", "Day", "WeekDay", "Snow", "Holiday" from dbo.rental_data where Year < 2015'
, @input_data_1_name = N'rental_train_data'
, @params = N'@trained_model varbinary(max) OUTPUT'
, @trained_model = @trained_model OUTPUT;
END;
GO
TRUNCATE TABLE rental_rx_models;
--Script to call the stored procedure that generates the rxDTree model and save the model in a table in SQL Server
DECLARE @model VARBINARY(MAX);
EXEC generate_rental_rx_model @model OUTPUT;
INSERT INTO rental_rx_models (model_name, model) VALUES('rxDTree', @model);
SELECT * FROM rental_rx_models;
GO
--Stored procedure that takes model name and new data as inout parameters and predicts the rental count for the new data
DROP PROCEDURE IF EXISTS predict_rentals;
GO
CREATE PROCEDURE predict_rentals (@model VARCHAR(100),@q NVARCHAR(MAX))
AS
BEGIN
DECLARE @rx_model VARBINARY(MAX) = (SELECT model FROM rental_rx_models WHERE model_name = @model);
EXECUTE sp_execute_external_script
@language = N'R'
, @script = N'
require("RevoScaleR");
#The InputDataSet contains the new data passed to this stored proc. We will use this data to predict.
rentals = InputDataSet;
#Convert types to factors
rentals$Holiday = factor(rentals$Holiday);
rentals$Snow = factor(rentals$Snow);
rentals$WeekDay = factor(rentals$WeekDay);
#Before using the model to predict, we need to unserialize it
rental_model = unserialize(rx_model);
#Call prediction function
rental_predictions = rxPredict(rental_model, rentals);'
, @input_data_1 = @q
, @output_data_1_name = N'rental_predictions'
, @params = N'@rx_model varbinary(max)'
, @rx_model = @rx_model
WITH RESULT SETS (("RentalCount_Predicted" FLOAT));
END;
GO
--Execute the predict_rentals stored proc and pass the modelname and a query string with a set of features we want to use to predict the rental count
EXEC dbo.predict_rentals @model = 'rxDTree',
@q ='SELECT CONVERT(INT, 3) AS Month, CONVERT(INT, 24) AS Day, CONVERT(INT, 4) AS WeekDay, CONVERT(INT, 1) AS Snow, CONVERT(INT, 1) AS Holiday';
GO
@@ -0,0 +1,71 @@
# Build a predictive model with SQL Server R Services
This sample shows how to create a predictive model in R and operationalize it with SQL Server 2016.
### Contents
[About this sample](#about-this-sample)<br/>
[Before you begin](#before-you-begin)<br/>
[Sample details](#sample-details)<br/>
[Related links](#related-links)<br/>
<a name=about-this-sample></a>
## About this sample
Predictive modeling is a powerful way to add intelligence to your application. It enables applications to predict outcomes against new data.
The act of incorporating predictive analytics into your applications involves two major phases:
model training and model operationalization.
In this sample, you will learn how to create a predictive model in R and operationalize it with SQL Server 2016.
Follow the step by step tutorial [here](http://aka.ms/sqldev/R) to walk through this sample.
<!-- Delete the ones that don't apply -->
- **Applies to:** SQL Server 2016 (or higher)
- **Key features:**
- **Workload:** SQL Server R Services
- **Programming Language:** T-SQL, R
- **Authors:** Nellie Gustafsson
- **Update history:** Getting started tutorial for R Services
<a name=before-you-begin></a>
## Before you begin
To run this sample, you need the following prerequisites.
Section 1 in the [tutorial](http://aka.ms/sqldev/R) covers the prerequisites.
After that, you can download a DB backup file and restore it using Setup.sql. [Download DB](https://deve2e.azureedge.net/sqlchoice/static/TutorialDB.bak)
**Software prerequisites:**
<!-- Examples -->
1. SQL Server 2016 (or higher) with R Services installed
2. SQL Server Management Studio
3. R IDE Tool like Visual Studio
<a name=sample-details></a>
## Sample Details
### PredictiveModel.R
The R script that generates a predictive model and uses it to predict rental counts
### PredictiveModel.SQL
Takes the R code in PredictiveModel.R and uses it inside SQL Server. Creating stored procedures for training and prediction.
<a name=related-links></a>
## Related Links
<!-- Links to more articles. Remember to delete "en-us" from the link path. -->
For additional content, see these articles:
[SQL Server R Services - Upgrade and Installation FAQ](https://msdn.microsoft.com/en-us/library/mt653951.aspx)
[Other SQL Server R Services Tutorials](https://msdn.microsoft.com/en-us/library/mt591993.aspx)
@@ -0,0 +1,13 @@
-- Before we start, we need to restore the DB for this tutorial.
-- Step1: Download the compressed backup file (https://deve2e.azureedge.net/sqlchoice/static/TutorialDB.bak)
--Save the file on a location where SQL Server can access it. For example: C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\
-- In a new query window in SSMS, execute the following restore statement, but REMEMBER TO CHANGE THE FILE PATHS
-- to match the directories of your installation!
USE master;
GO
RESTORE DATABASE TutorialDB
FROM DISK = 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Backup\TutorialDB.bak'
WITH
MOVE 'TutorialDB' TO 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\TutorialDB.mdf'
,MOVE 'TutorialDB_log' TO 'C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\TutorialDB.ldf';
GO
@@ -409,7 +409,7 @@ GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[PerformETL]
CREATE PROCEDURE [dbo].[PerformETL]
AS
BEGIN
INSERT INTO [dbo].[LoanStats]
+5 -2
View File
@@ -1,8 +1,11 @@
# Samples for SQL Server R Services
[Implementing Predictive Analytics](Implementing Predictive Analytics)
Go to the getting started tutorials to learn more about:
[Predictive Modeling with R Services](https://www.microsoft.com/en-us/sql-server/developer-get-started/rprediction)
[Customer Clustering with R Services](https://www.microsoft.com/en-us/sql-server/developer-get-started/rclustering)
Step-by-step sample that explains the basics about predictive analytics for developers. The lab will take you about 15 min and will show you how to create an application that uses node.js and SQL Server 2016 to predict if a cab driver will be tipped or not.
[Telco Customer Churn](Telco Customer Churn)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

@@ -1,83 +0,0 @@
# Clustering in R (SQL Server 2016)
In this code snack, developers will experience authoring R code to help them run a clustering exercise that “magically” groups data into distinct populations by using an unsupervised clustering algorithm, k-means. The k-means script will be packaged within a SQL stored procedure for convenient execution from a .NET application.
## About Clustering
The goal of a clustering algorithm is to look at an input set of data and attempt to identify groups of data by virtue of the similarity between the features of each example in the data set. What makes clustering algorithms particularly powerful is that they do not need a training step like the other algorithms— you simply provide them the data, tell them how many clusters you want to create and they assign each example to a group. The canonical clustering algorithm is k-means.
## Requirements
- Visual Studio 2015 with Update 3 (or later)
- SQL Server 2016 Developer Edition (or higher)
## Required SQL Server Configuration
- Make sure that your installation of SQL Server includes R Services, see [https://msdn.microsoft.com/en-us/library/mt696069.aspx](https://msdn.microsoft.com/en-us/library/mt696069.aspx)
- Using SQL Server Configuration Manager (which is launched from the Start menu), make sure that TCP/IP connections are enabled to your instance of SQL Server (under SQL Server Network Configuration).
![alt text][SQL Config]
[SQL Config]: Images/SqlConfig.png "SQL Server Network Configuration"
- Be sure that the SQL Server, SQL Server Launchpad and SQL Server Browser services are all running.
## Clone the provided project
Clone this repo on to your local machine.
The recommended path is C:\Clustering in R
## Create the database and tables
1. Open the ClusteringConsole.sln solution using Visual Studio 2015
2. From Solution Explorer, expand the ClusteringConsole solution, then Solution Items folder and open “Create Sample Database.sql”.
3. Adjust the file path for the FROM clause in the BULK INSERT statement if you cloned the project to a different location.
4. Select the Execute button
5. In the Connect dialog, provide your server name, authentication mode, username and password (as appropriate).
6. Wait for the script to complete successfully.
## Create the Clustering Stored Procedure
1. Within Visual Studio, open “Create Procedure ClusterTaxiData.sql”.
This stored procedure queries the data in the nyctaxi_features table and creates four clusters of data based on the passenger_count (the number of passengers in the taxi cab) and direct_distance (the distance traveled, measured as the crow flies). It uses the rxKmeans method to accomplish this, which runs the K-Means algorithm to group the data into the configured number of clusters (four clusters in this case). The formula syntax "~ passenger_count + direct_distance” used in the first parameter simply means to cluster around those two columns from the input data.
```
CREATE PROCEDURE [dbo].[ClusterTaxiData]
AS
BEGIN
DECLARE @inquery nvarchar(max) = N'
select tipped, passenger_count, trip_time_in_secs, trip_distance, direct_distance
from nyctaxi_features
'
EXEC sp_execute_external_script
@language = N'R',
@script = N'
## Cluster the data
clusters <- rxKmeans(~ passenger_count + direct_distance, data = InputDataSet, numClusters = 4, algorithm = "lloyd")
## Return the result (by convention the result data set is retrieved from a variable named OutputDataSet).
OutputDataSet <- as.data.frame(clusters$centers) ;
',
@input_data_1 = @inquery
WITH RESULT SETS ((passenger_count real, direct_distance real))
;
END
GO
```
2. Execute the script to create the stored procedure.
## Execute the Clustering Stored Procedure
1. Within Visual Studio, open “Execute Procedure ClusterTaxiData.sql”.
2. Select the Execute button
3. In the Connect dialog, provide your server name, authentication mode, username and password (as appropriate).
4. Wait for the script to complete successfully.
5. Observe the results, you should have four clusters of data, each a row in the results. You might interpret these results in order as short trips with one passenger, long trips with two passengers, moderate trips with two passengers and short trips with lots of passengers.
![alt text][Clustering Results]
[Clustering Results]: Images/ClusteringResults.png "Clustering Results"
## Leverage Clustering from an Application
1. Within Visual Studio, open app.config located underneath the SqlSecurity project in Solution Explorer.
2. Set the connectionString value so that it points to your SQL Server.
3. Save the file.
4. From the Debug menu, select Start Without Debugging.
Observe the clusters for the taxi rides as retrieved by the application, you have now integrated machine learning into your console application!
![alt text][Application Results]
[Application Results]: Images/ApplicationResults.png "Application Results"
@@ -1 +0,0 @@
Coming soon
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

@@ -1,203 +0,0 @@
# Dynamic Data Masking & Row Level Security Code Snack
In this code snack, developers will create a database having human resources data, including a table containing simulated employee pay data. They will be guided thru the sample data to highlight the sensitive information it contains (e.g., social security numbers and salaries) and then configuring the masking of the sensitive data. In addition, they will enable Row Level Security to handle three different roles: contractors (who have no visibility to any rows except their own in the table), HR (who can view all employee rows except those of executives) and Executives (who can view all employee rows). They will complete a node.js application that queries the database to see the differing outcomes that result based on Row Level Security policy.
## Requirements
- [Visual Studio Code](https://code.visualstudio.com/download)
- [SQL Server on Linux](https://www.microsoft.com/en-us/sql-server/sql-server-on-linux)
- [Node.js](https://nodejs.org/en/download/)
- [MSSQL extension for VS Code](https://marketplace.visualstudio.com/items?itemName=sanagama.vscode-mssql)
## Clone the provided project
Clone this repo on to your local machine.
## Configure your connection to SQL on Linux
1. Launch Visual Studio Code and open the project folder in Visual Studio Code.
2. From Code, Preferences select User Settings.
3. In between the curly braces, paste the following which adds two connection to SQL Server on Linux that the MSSQL extension will utilize:
```
"vscode-mssql.connections":
[
{
// connection 1
// All required inputs are present. No prompts when you choose this connection from the picklist.
"server": "localhost",
"database": "master",
"user": "sa",
"password": "Abc1234567890"
},
{
// connection 2
// All required inputs are present. No prompts when you choose this connection from the picklist.
"server": "localhost",
"database": "AdventureWorks",
"user": "Contractor",
"password": "Abc!1234"
}
]
```
4. Update the server for both of the above entries to match your SQL on Linux server.
5. Update the password for the first entry (sa user) so it is set to the value used by your SA user.
6. Leave the password as is for the second connection (contractor user). You will create and use this user later in the steps.
## Create the database and tables
1. Launch Visual Studio Code and open the project folder in Visual Studio Code.
2. Open “Create Sample Database.sql” underneath the SQL Resources folder.
3. Bring up the Command Palette (cmd+shift+P on Mac)
4. Type mssql
![alt text][mssqlcmd]
[mssqlcmd]: Images/mssqlcommand.png "mssql command"
5. Choose Connect to database
6. Choose the localhost using your SA user
![alt text][mssqlcmd3]
[mssqlcmd3]: Images/mssqlchoose.png "mssql choose connection"
7. Back in the document editor for Create Sample Database.sql, make sure you have nothing highlighted and execute it with cmd+shift+e
8. Wait for the script to complete successfully.
## Explore the Sample Data
1. Within Visual Studio Code, open “Explore Data.sql"
2. Execute the script to observe the sensitive fields in this query that summarize the pay rate for employees: NationalIDNumber (e.g., social security number) and Rate (e.g., pay rate)
3. Notice the employee table has the NationalIDNumber which is sensitive field and the EmployePayHistory table has the Rate field which is sensitive because it captures the employees rate of pay.
![alt text][Explore Data]
[Explore Data]: Images/mssqlunmaskedresults.png "Explore Data"
4. Click the X to close the MSSQL Output tab. In the steps that follow, remember to close this anytime you will execute a new query or you may not see the results of your latest query.
## Configure Masking
1. Within Visual Studio Code, open “Configure Masking.sql"
2. Execute the script to create mask both the NationalIDNumber and Rate fields.
```
-- Mask the NationalIDNumber column so it only displays the last two digits of field (for example: XX-XXX-XX43)
ALTER TABLE HumanResources.Employee
ALTER COLUMN NationalIDNumber ADD MASKED WITH(FUNCTION = 'partial(0,"XX-XXX-XX",2)')
-- Mask the rate by providing a random value in place of the actual rate
ALTER TABLE HumanResources.EmployeePayHistory
ALTER COLUMN Rate ADD MASKED WITH (FUNCTION = 'random(20,150)')
```
3. Return to “Explore Data.sql” and execute this script again.
4. Observe that even though you enabled masking on the table, these fields are still available to you (the administrative user) in their original unmasked format.
5. To view the results with the masks applied, create a new user who can query from the database who does not have priveleges to see the unmasked data (in other words, they will always see the masked data).
6. Open “Create Contractor User.sql” and execute it to create a new login and user with the name Contractor and password Abc!1234.
7. Return to “Explore Data.sql” and execute this script again.
8. Bring up the Command Palette (cmd+shift+P on Mac)
9. Type mssql
10. Choose Connect to database
11. Choose the localhost using your Contractor user
12. Execute this script again.
13. Observe that now the NationalIDNumber only displays the last two digits, and the Rate values are different from before.
![alt text][Masked Data]
[Masked Data]: Images/mssqlmaskedresults.png "Masked Data"
## Configure Row Level Security
1. Next, consider the scenario where you want to enforce a policy where only Executive users in the organization can see all employee rows in the Employee table. Users in the Human resources department can see all rows except those of the executives. Finally, all other users can only see their row.
2. This is something you can accomplish using Row Level Security in a fashion that “just works” and applies transparently to the user issuing the query.
3. Within Visual Studio Code, change your localhost connection back to use the SA user.
4. Open “Configure Row Level Security.sql”.
5. Execute the script. This will create a schema (to hold our security related functions), a predicate function that filters the rows based upon the user performing the querying, and a policy that is applied to the Employee table that uses the predicate function to filter the result set to only the rows the user should be seeing.
```
-- Best practice, create a schema to hold security predicates
CREATE SCHEMA Security;
GO
-- Create the predicate function
CREATE FUNCTION Security.LimitAccess(@LoginID nvarchar(256), @OrganizationLevel smallint)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 as LimitAccess_Result
FROM HumanResources.EmployeeDepartmentHistory deptHist INNER JOIN HumanResources.Employee emp
ON deptHist.BusinessEntityID = emp.BusinessEntityID
WHERE (emp.LoginID = CAST(SESSION_CONTEXT(N'LoginID') AS nvarchar(256)) AND EndDate is NULL AND DepartmentID = 9 AND @OrganizationLevel > 1) OR
(emp.LoginID = CAST(SESSION_CONTEXT(N'LoginID') AS nvarchar(256)) AND EndDate is NULL AND DepartmentID = 16) OR
CAST(SESSION_CONTEXT(N'LoginID') AS nvarchar(256)) = @LoginID;
GO
-- Create a policy that applies the predicate
CREATE SECURITY POLICY Security.HumanResourcesPolicy
ADD FILTER PREDICATE Security.LimitAccess(LoginID, OrganizationLevel) ON HumanResources.Employee
WITH (STATE = ON);
GO
```
6. Now, open “Explore Data with RLS.sql”. This query will show the differing result sets that appear for different users when the policy is in action. Notice that this query does not rely on the credentials used to connect to SQL Server, but rather the login store in the session context.
7. This is a useful pattern when you have an application that uses one connection string to SQL Server, but is operating in an environment where your application handles the login, and that login is different from the credentials used to access SQL Server. You can uses this application login information to inform the Row Level Security policy.
```
-- SET the context to Contractor user and make it immutable for the duration of the connection
EXEC sp_set_session_context @key=N'LoginID', @value=N'adventure-works\lynn0';
-- Query the table as usual: observe that only the one row is returned
SELECT * FROM HumanResources.Employee;
```
8. Execute the query. Observe the different result sets that appear for the exact same query— they are made different only because of the LoginID session context provided.
![alt text][RLS Data]
[RLS Data]: Images/rlsresults.png "RLS Data"
## Leverage Row Level Security from an Application
1. Lets put Row Level Security to work within the context of an application, in this case a node.js application.
2. Within Visual Studio Code, open SqlClient.js located underneath the SqlSecurity folder root.
3. Near the top, modify the values of the config element so that they contain the appropriate values to connect to your instance of the database.
```
// Provide the connection details appropriate to your environment
var config = {
userName: 'sa',
password: 'Abc1234567890',
server: 'localhost',
options: {
database: 'adventureworks',
encrypt: true
},
loginID : 'adventure-works\\ken0'
};
```
4. Save the file.
5. Open an instance of Terminal and navigate to the directory that contains SqlClient.js.
6. Execute the following to install the tediuos package:
```
npm install tedious
```
6. Now run the node.js app:
```
node SqlClient.js
```
7. Observe the query that is run and that only 1 row is returned (the row for that user in the employee table).
```
$ node SqlClient.js
Connected.
Executing query: SELECT Count(*) FROM [HumanResources].[Employee]
= 1
1 rows
```
8. Experiment with the other users to see the differing query counts that are returned. In SqlClient.js modify the config object, loginID value to either 'adventure-works\\paula0' or 'adventure-works\\ken0'.
```
var config = {
userName: 'sa',
password: 'Abc1234567890',
server: 'localhost',
options: {
database: 'adventureworks',
encrypt: true
},
loginID : 'adventure-works\\paula0'
};
```
8. Run the node application again as previously shown.
9. Observe that the same query is run as before, but either 283 rows or 290 row are returned depending on the loginID used.
@@ -1,11 +0,0 @@
USE [AdventureWorks]
GO
-- Mask the NationalIDNumber column so it only displays the last two digits of field (for example: XX-XXX-XX43)
ALTER TABLE HumanResources.Employee
ALTER COLUMN NationalIDNumber ADD MASKED WITH(FUNCTION = 'partial(0,"XX-XXX-XX",2)')
-- Mask the rate by providing a random value in place of the actual rate
ALTER TABLE HumanResources.EmployeePayHistory
ALTER COLUMN Rate ADD MASKED WITH (FUNCTION = 'random(20,150)')
@@ -1,27 +0,0 @@
USE [AdventureWorks]
GO
-- Best practice, create a schema to hold security predicates
CREATE SCHEMA Security;
GO
-- Create the predicate function
CREATE FUNCTION Security.LimitAccess(@LoginID nvarchar(256), @OrganizationLevel smallint)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 as LimitAccess_Result
FROM HumanResources.EmployeeDepartmentHistory deptHist INNER JOIN HumanResources.Employee emp
ON deptHist.BusinessEntityID = emp.BusinessEntityID
WHERE (emp.LoginID = CAST(SESSION_CONTEXT(N'LoginID') AS nvarchar(256)) AND EndDate is NULL AND DepartmentID = 9 AND @OrganizationLevel > 1) OR
(emp.LoginID = CAST(SESSION_CONTEXT(N'LoginID') AS nvarchar(256)) AND EndDate is NULL AND DepartmentID = 16) OR
CAST(SESSION_CONTEXT(N'LoginID') AS nvarchar(256)) = @LoginID;
GO
-- Create a policy that applies the predicate
CREATE SECURITY POLICY Security.HumanResourcesPolicy
ADD FILTER PREDICATE Security.LimitAccess(LoginID, OrganizationLevel) ON HumanResources.Employee
WITH (STATE = ON);
GO
@@ -1,19 +0,0 @@
USE [master]
GO
CREATE LOGIN [Contractor] WITH PASSWORD=N'Abc!1234',
DEFAULT_DATABASE=[AdventureWorks],
DEFAULT_LANGUAGE=[us_english],
CHECK_EXPIRATION=OFF,
CHECK_POLICY=OFF
GO
USE [AdventureWorks]
GO
CREATE USER [Contractor] FOR LOGIN [Contractor]
WITH DEFAULT_SCHEMA=[dbo]
GO
ALTER ROLE [db_datareader] ADD MEMBER [Contractor]
GO
@@ -1,6 +0,0 @@
ALTER DATABASE AdventureWorks
SET SINGLE_USER --or RESTRICTED_USER
WITH ROLLBACK IMMEDIATE;
GO
DROP DATABASE AdventureWorks;
@@ -1,29 +0,0 @@
USE [AdventureWorks]
GO
-- Query the table as usual: observe that no rows are returned because no login ID context is set
SELECT * FROM HumanResources.Employee;
-- SET the context to Contractor user and make it immutable for the duration of the connection
EXEC sp_set_session_context @key=N'LoginID', @value=N'adventure-works\lynn0';
-- Query the table as usual: observe that only the one row is returned
SELECT * FROM HumanResources.Employee;
-- SET the context to an HR user
EXEC sp_set_session_context @key=N'LoginID', @value=N'adventure-works\paula0';
-- Query the table as usual: observe that only rows with OrganizationLevel of 2 or greater are returned
SELECT * FROM HumanResources.Employee;
-- SET the context to an Executive user
EXEC sp_set_session_context @key=N'LoginID', @value=N'adventure-works\ken0';
-- Query the table as usual: observe that all rows are returned
SELECT * FROM HumanResources.Employee;
@@ -1,7 +0,0 @@
USE [AdventureWorks]
GO
-- Observe the sensitive fields in this query that summarize the pay rate for employees: NationalIDNumber, Rate
SELECT TOP 10 emp.BusinessEntityID, NationalIDNumber, JobTitle, RateChangeDate, Rate
FROM HumanResources.Employee emp INNER JOIN HumanResources.EmployeePayHistory pay
ON emp.BusinessEntityID = pay.BusinessEntityId;
@@ -1,87 +0,0 @@
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;
// Provide the connection details appropriate to your environment
var config = {
userName: 'sa',
password: 'Abc1234567890',
server: 'localhost',
options: {
database: 'adventureworks',
encrypt: true
},
loginID : 'adventure-works\\lynn0'
};
var connection = new Connection(config);
connection.on('connect', function(err) {
if (err)
{
console.log("Unable to Connect: " + err);
return;
}
// If no error, then good to go...
console.log("Connected.");
executeSetSessionStatement();
});
function executeSetSessionStatement() {
// Specify the name of the predictive stored procedure
storedProcedureName = "sp_set_session_context";
request = new Request(storedProcedureName, function(err, rowCount) {
if (err) {
console.log(err);
} else {
// Invoke the query in the session context
executeQuery();
}
});
// The input values to the prediction are provided here:
request.addParameter('@key', TYPES.VarChar, 'LoginID');
request.addParameter('@value', TYPES.VarChar, config.loginID);
request.addParameter('@readonly', TYPES.Int, '1');
// Iterate over any received rows in the result
request.on('row', function(columns) {
console.log("Session login set")
columns.forEach(function(column) {
console.log(column.metadata.colName + " = " + column.value);
});
});
connection.callProcedure(request);
}
function executeQuery() {
// Specify the name of the predictive stored procedure
sqlQuery = "SELECT Count(*) FROM [HumanResources].[Employee]";
request = new Request(sqlQuery, function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
});
// Iterate over any received rows in the result
request.on('row', function(columns) {
columns.forEach(function(column) {
console.log(column.metadata.colName + " = " + column.value);
});
});
console.log("Executing query: " + sqlQuery);
connection.execSql(request);
}
@@ -1,21 +0,0 @@
The MIT License
Copyright (c) 2010-2011 Mike D Pilsbury
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -1,52 +0,0 @@
# Tedious (node implementation of TDS)
[![Dependency Status](https://david-dm.org/pekim/tedious.svg)](https://david-dm.org/pekim/tedious) [![NPM version](https://badge.fury.io/js/tedious.svg)](http://badge.fury.io/js/tedious) [![Build Status](https://secure.travis-ci.org/pekim/tedious.svg)](http://travis-ci.org/pekim/tedious) [![Build Status](https://ci.appveyor.com/api/projects/status/ike3p58hljpyffrl?svg=true)](https://ci.appveyor.com/project/pekim/tedious)
Tedious is an implementation of the [TDS protocol](http://msdn.microsoft.com/en-us/library/dd304523.aspx),
which is used to interact with instances of Microsoft's SQL Server. It is intended to be a fairly slim implementation of the protocol, with not too much additional functionality.
**NOTE: New columns are nullable by default as of version 1.11.0**
Previous behavior can be restored using `config.options.enableAnsiNullDefault = false`. See [pull request 230](https://github.com/pekim/tedious/pull/230).
**NOTE: Default login behavior has changed slightly as of version 1.2**
See the [changelog](http://pekim.github.io/tedious/changelog.html) for version history.
### Supported TDS versions
- TDS 7.4 (SQL Server 2012/2014)
- TDS 7.3.B (SQL Server 2008 R2)
- TDS 7.3.A (SQL Server 2008)
- TDS 7.2 (SQL Server 2005)
- TDS 7.1 (SQL Server 2000)
## Installation
npm install tedious
<a name="documentation" />
## Documentation
More documentation is available at [pekim.github.io/tedious/](http://pekim.github.io/tedious/)
<a name="discussion" />
## Discussion
Google Group - http://groups.google.com/group/node-tedious
<a name="name" />
## Name
_Tedious_ is simply derived from a fast, slightly garbled, pronunciation of the letters T, D and S.
<a name="license" />
## Licence
Copyright (c) 2010-2014 Mike D Pilsbury
The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,52 +0,0 @@
version: "{build}"
environment:
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "5"
branches:
only:
- master
- /^maint\/.+/
- /v\d+\.\d+\.\d+/
install:
- ps: Install-Product node $env:nodejs_version
- npm install
cache:
- node_modules
build: off
before_test:
- sc config sqlbrowser start= auto
- net start sqlbrowser
- mkdir C:\Users\appveyor\.tedious
test_script:
- node --version
- npm --version
- cmd: |
SET EXITVAL=0
npm run-script test || SET EXITVAL=1
powershell %cd%\scripts\appveyor\config.ps1 SQL2008R2SP2
npm run-script test-integration || SET EXITVAL=1
net stop MSSQL$SQL2008R2SP2
powershell %cd%\scripts\appveyor\config.ps1 SQL2012SP1
npm run-script test-integration || SET EXITVAL=1
net stop MSSQL$SQL2012SP1
powershell %cd%\scripts\appveyor\config.ps1 SQL2014
npm run-script test-integration || SET EXITVAL=1
net stop MSSQL$SQL2014
EXIT /B %EXITVAL%
@@ -1,33 +0,0 @@
# Tedious Benchmarks
This folder contains a collection of benchmarks for `tedious`.
Running all existing benchmarks is easy, just execute the following from
inside the `tedious` root folder:
```sh
node benchmarks
```
**NOTE:** The benchmarks try to load `tedious` code from `lib`, so make sure
you run `npm run prepublish` first.
This will serially execute every available benchmark test in a
seperate Node.js process. Running each benchmark in a separate process
ensures that each benchmark is run with a clean slate.
You can also execute a specific benchmarks:
```sh
node benchmarks/<type>/<benchmark-name>
```
The benchmarks are executed by using the `benchmark` module. Unfortunately,
this module can add a bit of useless noise when trying to collect profiling
information. To reduce this noise, benchmarks can be run in a special "profile"
mode. This will execute the benchmark's code without making use of `benchmark`
library and with a fixed number of iterations.
```sh
node benchmarks/<type>/<benchmark-name> --profile
```
@@ -1,82 +0,0 @@
"use strict";
var fs = require("fs");
var async = require("async");
var Benchmark = require("benchmark");
var Connection = require("../lib/tedious").Connection;
function createConnection(cb) {
var config = JSON.parse(fs.readFileSync(process.env.HOME + '/.tedious/test-connection.json', 'utf8')).config;
var connection = new Connection(config);
connection.on("connect", function() {
cb(connection);
});
}
function createBenchmark(test) {
if (process.argv.indexOf("--profile") != -1) {
process.nextTick(function() {
runProfile(test);
});
} else {
process.nextTick(function() {
runBenchmark(test);
});
}
}
function runBenchmark(test) {
var memStart, memMax = memStart = process.memoryUsage().rss;
test.setup(function(err) {
if (err) throw err;
var bm = new Benchmark(test.name, {
defer: true,
fn: function(deferred) {
test.exec(function(err) {
if (err) throw err;
memMax = Math.max(memMax, process.memoryUsage().rss);
deferred.resolve();
});
}
});
bm.on("complete", function(event) {
console.log(String(event.target))
console.log("Memory:", (memMax - memStart)/1024/1024, "MiB")
test.teardown(function(err) {
if (err) throw err;
});
});
bm.run({ "async": true });
});
}
function runProfile(test) {
test.setup(function(err) {
if (err) throw err;
async.timesSeries(test.profileIterations, function(n, done) {
async.setImmediate(function() {
console.log("[Iteration " + n + "]");
test.exec(done);
});
}, function(err) {
if (err) throw err;
test.teardown(function(err) {
if (err) throw err;
});
});
});
}
module.exports.createBenchmark = createBenchmark;
module.exports.createConnection = createConnection;
@@ -1,34 +0,0 @@
var fs = require("fs");
var path = require("path");
var childProcess = require("child_process")
var Benchmark = require("benchmark");
var Connection = require("../lib/tedious").Connection;
var Request = require("../lib/tedious").Request;
var types = ["query", "token-parser"];
var tests = [];
types.forEach(function(type) {
var dir = path.join(__dirname, type);
tests.push.apply(tests, fs.readdirSync(dir).map(function(file) {
return path.join(dir, file);
}));
});
runBenchmarks();
function runBenchmarks() {
var test = tests.shift();
if (!test)
return;
var child = childProcess.spawn(process.execPath, [ test ], { stdio: 'inherit' });
child.on('close', function(code) {
if (code) {
process.exit(code);
} else {
runBenchmarks();
}
});
}
@@ -1,49 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var async = require("async");
var common = require("../common");
var connection;
common.createBenchmark({
name: "Many result rows",
profileIterations: 10,
setup: function(cb) {
common.createConnection(function(_connection) {
connection = _connection;
var request = new Request("CREATE TABLE #benchmark ([id] int IDENTITY(1,1), [name] nvarchar(100), [description] nvarchar(max))", function(err) {
if (err) return cb(err);
async.timesSeries(10000, function(n, next) {
var request = new Request("INSERT INTO #benchmark ([name], [description]) VALUES (@name, @description)", next);
request.addParameter("name", TYPES.NVarChar, "Row " + n);
request.addParameter("description", TYPES.NVarChar, "Example Test Description for Row " + n);
connection.execSql(request);
}, cb);
});
connection.execSqlBatch(request);
});
},
exec: function(cb) {
var request = new Request("SELECT * FROM #benchmark", cb);
connection.execSql(request);
},
teardown: function(cb) {
var request = new Request("DROP TABLE #benchmark", function(err) {
if (err) {
return cb(err);
}
connection.close();
});
connection.execSqlBatch(request);
}
});
@@ -1,44 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var common = require("../common");
var connection;
common.createBenchmark({
name: "inserting nvarchar(max) with 5242880 chars",
profileIterations: 100,
setup: function(cb) {
common.createConnection(function(_connection) {
connection = _connection;
var request = new Request("CREATE TABLE #benchmark ([value] nvarchar(max))", function(err) {
if (err) return cb(err);
var request = new Request("INSERT INTO #benchmark ([value]) VALUES (@value)", cb);
request.addParameter("value", TYPES.NVarChar, new Array(5 * 1024 * 1024).join("x"));
connection.execSql(request);
});
connection.execSqlBatch(request);
});
},
exec: function(cb) {
var request = new Request("SELECT * FROM #benchmark", cb);
connection.execSql(request);
},
teardown: function(cb) {
var request = new Request("DROP TABLE #benchmark", function(err) {
if (err) {
return cb(err);
}
connection.close();
});
connection.execSqlBatch(request);
}
});
@@ -1,44 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var common = require("../common");
var connection;
common.createBenchmark({
name: "inserting nvarchar(max) with 4 chars",
profileIterations: 10000,
setup: function(cb) {
common.createConnection(function(_connection) {
connection = _connection;
var request = new Request("CREATE TABLE #benchmark ([value] nvarchar(max))", function(err) {
if (err) return cb(err);
var request = new Request("INSERT INTO #benchmark ([value]) VALUES (@value)", cb);
request.addParameter("value", TYPES.NVarChar, "asdf");
connection.execSql(request);
});
connection.execSqlBatch(request);
});
},
exec: function(cb) {
var request = new Request("SELECT * FROM #benchmark", cb);
connection.execSql(request);
},
teardown: function(cb) {
var request = new Request("DROP TABLE #benchmark", function(err) {
if (err) {
return cb(err);
}
connection.close();
});
connection.execSqlBatch(request);
}
});
@@ -1,42 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var common = require("../common");
var connection;
common.createBenchmark({
name: "inserting varbinary(4) with 4 bytes",
setup: function(cb) {
common.createConnection(function(_connection) {
connection = _connection;
var request = new Request("CREATE TABLE #benchmark ([value] varbinary(4))", function(err) {
if (err) return cb(err);
var request = new Request("INSERT INTO #benchmark ([value]) VALUES (@value)", cb);
request.addParameter("value", TYPES.VarBinary, new Buffer("asdf"));
connection.execSql(request);
});
connection.execSqlBatch(request);
});
},
exec: function(cb) {
var request = new Request("SELECT * FROM #benchmark", cb);
connection.execSql(request);
},
teardown: function(cb) {
var request = new Request("DROP TABLE #benchmark", function(err) {
if (err) {
return cb(err);
}
connection.close();
});
connection.execSqlBatch(request);
}
});
@@ -1,46 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var common = require("../common");
var connection;
common.createBenchmark({
name: "inserting varbinary(max) with 50 MiB",
profileIterations: 20,
setup: function(cb) {
common.createConnection(function(_connection) {
connection = _connection;
var request = new Request("CREATE TABLE #benchmark ([value] varbinary(max))", function(err) {
if (err) return cb(err);
var request = new Request("INSERT INTO #benchmark ([value]) VALUES (@value)", cb);
var buf = new Buffer(50 * 1024 * 1024);
buf.fill("x");
request.addParameter("value", TYPES.VarBinary, buf);
connection.execSql(request);
});
connection.execSqlBatch(request);
});
},
exec: function(cb) {
var request = new Request("SELECT * FROM #benchmark", cb);
connection.execSql(request);
},
teardown: function(cb) {
var request = new Request("DROP TABLE #benchmark", function(err) {
if (err) {
return cb(err);
}
connection.close();
});
connection.execSqlBatch(request);
}
});
@@ -1,46 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var common = require("../common");
var connection;
common.createBenchmark({
name: "inserting varbinary(max) with 5 MiB",
profileIterations: 100,
setup: function(cb) {
common.createConnection(function(_connection) {
connection = _connection;
var request = new Request("CREATE TABLE #benchmark ([value] varbinary(max))", function(err) {
if (err) return cb(err);
var request = new Request("INSERT INTO #benchmark ([value]) VALUES (@value)", cb);
var buf = new Buffer(5 * 1024 * 1024);
buf.fill("x");
request.addParameter("value", TYPES.VarBinary, buf);
connection.execSql(request);
});
connection.execSqlBatch(request);
});
},
exec: function(cb) {
var request = new Request("SELECT * FROM #benchmark", cb);
connection.execSql(request);
},
teardown: function(cb) {
var request = new Request("DROP TABLE #benchmark", function(err) {
if (err) {
return cb(err);
}
connection.close();
});
connection.execSqlBatch(request);
}
});
@@ -1,42 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var common = require("../common");
var connection;
common.createBenchmark({
name: "inserting varbinary(max) with 4 bytes",
setup: function(cb) {
common.createConnection(function(_connection) {
connection = _connection;
var request = new Request("CREATE TABLE #benchmark ([value] varbinary(max))", function(err) {
if (err) return cb(err);
var request = new Request("INSERT INTO #benchmark ([value]) VALUES (@value)", cb);
request.addParameter("value", TYPES.VarBinary, new Buffer("asdf"));
connection.execSql(request);
});
connection.execSqlBatch(request);
});
},
exec: function(cb) {
var request = new Request("SELECT * FROM #benchmark", cb);
connection.execSql(request);
},
teardown: function(cb) {
var request = new Request("DROP TABLE #benchmark", function(err) {
if (err) {
return cb(err);
}
connection.close();
});
connection.execSqlBatch(request);
}
});
@@ -1,42 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var Parser = require("../../lib/token/token-stream-parser").Parser;
var common = require("../common");
var parser = new Parser({ token: function() { } }, {}, {});
var tokenCount = 50;
var data = new Buffer(new Array(tokenCount).join("810300000000001000380269006400000000000900e7c8000904d00034046e0061006d006500000000000900e7ffff0904d000340b6400650073006300720069007000740069006f006e00"), "hex");
common.createBenchmark({
name: "parsing `COLMETADATA` tokens",
profileIterations: 3000,
setup: function(cb) {
cb();
},
exec: function(cb) {
var count = 0;
parser.on("columnMetadata", function() {
count += 1;
if (count === tokenCount - 1) {
parser.removeAllListeners("columnMetadata");
cb();
}
});
parser.addBuffer(data);
},
teardown: function(cb) {
cb();
}
});
@@ -1,42 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var Parser = require("../../lib/token/token-stream-parser").Parser;
var common = require("../common");
var parser = new Parser({ token: function() { } }, {}, {});
var tokenCount = 500;
var data = new Buffer(new Array(tokenCount).join("FE0000E0000000000000000000"), "hex");
common.createBenchmark({
name: "parsing `DONEPROC` tokens",
profileIterations: 3000,
setup: function(cb) {
cb();
},
exec: function(cb) {
var count = 0;
parser.on("doneProc", function() {
count += 1;
if (count === tokenCount - 1) {
parser.removeAllListeners("doneProc");
cb();
}
});
parser.addBuffer(data);
},
teardown: function(cb) {
cb();
}
});
@@ -1,366 +0,0 @@
var tedious = require("../../lib/tedious");
var Request = tedious.Request;
var TYPES = tedious.TYPES;
var Parser = require("../../lib/token/token-stream-parser").Parser;
var common = require("../common");
var parser = new Parser({ token: function() { } }, {}, {});
var data = new Buffer([
"810300000000001000380269006400000000000900E7C8000904D00034046E00",
"61006D006500000000000900E7FFFF0904D000340B6400650073006300720069",
"007000740069006F006E00D1010000000A0052006F0077002000300044000000",
"00000000440000004500780061006D0070006C00650020005400650073007400",
"20004400650073006300720069007000740069006F006E00200066006F007200",
"200052006F0077002000300000000000D1020000000A0052006F007700200031",
"004400000000000000440000004500780061006D0070006C0065002000540065",
"007300740020004400650073006300720069007000740069006F006E00200066",
"006F007200200052006F0077002000310000000000D1030000000A0052006F00",
"7700200032004400000000000000440000004500780061006D0070006C006500",
"2000540065007300740020004400650073006300720069007000740069006F00",
"6E00200066006F007200200052006F0077002000320000000000D1040000000A",
"0052006F007700200033004400000000000000440000004500780061006D0070",
"006C006500200054006500730074002000440065007300630072006900700074",
"0069006F006E00200066006F007200200052006F0077002000330000000000D1",
"050000000A0052006F0077002000340044000000000000004400000045007800",
"61006D0070006C00650020005400650073007400200044006500730063007200",
"69007000740069006F006E00200066006F007200200052006F00770020003400",
"00000000D1060000000A0052006F007700200035004400000000000000440000",
"004500780061006D0070006C0065002000540065007300740020004400650073",
"006300720069007000740069006F006E00200066006F007200200052006F0077",
"002000350000000000D1070000000A0052006F00770020003600440000000000",
"0000440000004500780061006D0070006C006500200054006500730074002000",
"4400650073006300720069007000740069006F006E00200066006F0072002000",
"52006F0077002000360000000000D1080000000A0052006F0077002000370044",
"00000000000000440000004500780061006D0070006C00650020005400650073",
"00740020004400650073006300720069007000740069006F006E00200066006F",
"007200200052006F0077002000370000000000D1090000000A0052006F007700",
"200038004400000000000000440000004500780061006D0070006C0065002000",
"540065007300740020004400650073006300720069007000740069006F006E00",
"200066006F007200200052006F0077002000380000000000D10A0000000A0052",
"006F007700200039004400000000000000440000004500780061006D0070006C",
"0065002000540065007300740020004400650073006300720069007000740069",
"006F006E00200066006F007200200052006F0077002000390000000000D10B00",
"00000C0052006F00770020003100300046000000000000004600000045007800",
"61006D0070006C00650020005400650073007400200044006500730063007200",
"69007000740069006F006E00200066006F007200200052006F00770020003100",
"300000000000D10C0000000C0052006F00770020003100310046000000000000",
"00460000004500780061006D0070006C00650020005400650073007400200044",
"00650073006300720069007000740069006F006E00200066006F007200200052",
"006F00770020003100310000000000D10D0000000C0052006F00770020003100",
"32004600000000000000460000004500780061006D0070006C00650020005400",
"65007300740020004400650073006300720069007000740069006F006E002000",
"66006F007200200052006F00770020003100320000000000D10E0000000C0052",
"006F0077002000310033004600000000000000460000004500780061006D0070",
"006C006500200054006500730074002000440065007300630072006900700074",
"0069006F006E00200066006F007200200052006F007700200031003300000000",
"00D10F0000000C0052006F007700200031003400460000000000000046000000",
"4500780061006D0070006C006500200054006500730074002000440065007300",
"6300720069007000740069006F006E00200066006F007200200052006F007700",
"20003100340000000000D1100000000C0052006F007700200031003500460000",
"0000000000460000004500780061006D0070006C006500200054006500730074",
"0020004400650073006300720069007000740069006F006E00200066006F0072",
"00200052006F00770020003100350000000000D1110000000C0052006F007700",
"2000310036004600000000000000460000004500780061006D0070006C006500",
"2000540065007300740020004400650073006300720069007000740069006F00",
"6E00200066006F007200200052006F00770020003100360000000000D1120000",
"000C0052006F0077002000310037004600000000000000460000004500780061",
"006D0070006C0065002000540065007300740020004400650073006300720069",
"007000740069006F006E00200066006F007200200052006F0077002000310037",
"0000000000D1130000000C0052006F0077002000310038004600000000000000",
"460000004500780061006D0070006C0065002000540065007300740020004400",
"650073006300720069007000740069006F006E00200066006F00720020005200",
"6F00770020003100380000000000D1140000000C0052006F0077002000310039",
"004600000000000000460000004500780061006D0070006C0065002000540065",
"007300740020004400650073006300720069007000740069006F006E00200066",
"006F007200200052006F00770020003100390000000000D1150000000C005200",
"6F0077002000320030004600000000000000460000004500780061006D007000",
"6C00650020005400650073007400200044006500730063007200690070007400",
"69006F006E00200066006F007200200052006F00770020003200300000000000",
"D1160000000C0052006F00770020003200310046000000000000004600000045",
"00780061006D0070006C00650020005400650073007400200044006500730063",
"00720069007000740069006F006E00200066006F007200200052006F00770020",
"003200310000000000D1170000000C0052006F00770020003200320046000000",
"00000000460000004500780061006D0070006C00650020005400650073007400",
"20004400650073006300720069007000740069006F006E00200066006F007200",
"200052006F00770020003200320000000000D1180000000C0052006F00770020",
"00320033004600000000000000460000004500780061006D0070006C00650020",
"00540065007300740020004400650073006300720069007000740069006F006E",
"00200066006F007200200052006F00770020003200330000000000D119000000",
"0C0052006F007700200032003400460000000000000046000000450078006100",
"6D0070006C006500200054006500730074002000440065007300630072006900",
"7000740069006F006E00200066006F007200200052006F007700200032003400",
"00000000D11A0000000C0052006F007700200032003500460000000000000046",
"0000004500780061006D0070006C006500200054006500730074002000440065",
"0073006300720069007000740069006F006E00200066006F007200200052006F",
"00770020003200350000000000D11B0000000C0052006F007700200032003600",
"4600000000000000460000004500780061006D0070006C006500200054006500",
"7300740020004400650073006300720069007000740069006F006E0020006600",
"6F007200200052006F00770020003200360000000000D11C0000000C0052006F",
"0077002000320037004600000000000000460000004500780061006D0070006C",
"0065002000540065007300740020004400650073006300720069007000740069",
"006F006E00200066006F007200200052006F00770020003200370000000000D1",
"1D0000000C0052006F0077002000320038004600000000000000460000004500",
"780061006D0070006C0065002000540065007300740020004400650073006300",
"720069007000740069006F006E00200066006F007200200052006F0077002000",
"3200380000000000D11E0000000C0052006F0077002000320039004600000000",
"000000460000004500780061006D0070006C0065002000540065007300740020",
"004400650073006300720069007000740069006F006E00200066006F00720020",
"0052006F00770020003200390000000000D11F0000000C0052006F0077002000",
"330030004600000000000000460000004500780061006D0070006C0065002000",
"540065007300740020004400650073006300720069007000740069006F006E00",
"200066006F007200200052006F00770020003300300000000000D1200000000C",
"0052006F0077002000330031004600000000000000460000004500780061006D",
"0070006C00650020005400650073007400200044006500730063007200690070",
"00740069006F006E00200066006F007200200052006F00770020003300310000",
"000000D1210000000C0052006F00770020003300320046000000000000004600",
"00004500780061006D0070006C00650020005400650073007400200044006500",
"73006300720069007000740069006F006E00200066006F007200200052006F00",
"770020003300320000000000D1220000000C0052006F00770020003300330046",
"00000000000000460000004500780061006D0070006C00650020005400650073",
"00740020004400650073006300720069007000740069006F006E00200066006F",
"007200200052006F00770020003300330000000000D1230000000C0052006F00",
"77002000330034004600000000000000460000004500780061006D0070006C00",
"6500200054006500730074002000440065007300630072006900700074006900",
"6F006E00200066006F007200200052006F00770020003300340000000000D124",
"0000000C0052006F007700200033003500460000000000000046000000450078",
"0061006D0070006C006500200054006500730074002000440065007300630072",
"0069007000740069006F006E00200066006F007200200052006F007700200033",
"00350000000000D1250000000C0052006F007700200033003600460000000000",
"0000460000004500780061006D0070006C006500200054006500730074002000",
"4400650073006300720069007000740069006F006E00200066006F0072002000",
"52006F00770020003300360000000000D1260000000C0052006F007700200033",
"0037004600000000000000460000004500780061006D0070006C006500200054",
"0065007300740020004400650073006300720069007000740069006F006E0020",
"0066006F007200200052006F00770020003300370000000000D1270000000C00",
"52006F0077002000330038004600000000000000200000004500780061006D00",
"70006C006500200054006500730074002000440065007300",
"260000006300720069007000740069006F006E00200066006F00720020005200",
"6F00770020003300380000000000D1280000000C0052006F0077002000330039",
"004600000000000000460000004500780061006D0070006C0065002000540065",
"007300740020004400650073006300720069007000740069006F006E00200066",
"006F007200200052006F00770020003300390000000000D1290000000C005200",
"6F0077002000340030004600000000000000460000004500780061006D007000",
"6C00650020005400650073007400200044006500730063007200690070007400",
"69006F006E00200066006F007200200052006F00770020003400300000000000",
"D12A0000000C0052006F00770020003400310046000000000000004600000045",
"00780061006D0070006C00650020005400650073007400200044006500730063",
"00720069007000740069006F006E00200066006F007200200052006F00770020",
"003400310000000000D12B0000000C0052006F00770020003400320046000000",
"00000000460000004500780061006D0070006C00650020005400650073007400",
"20004400650073006300720069007000740069006F006E00200066006F007200",
"200052006F00770020003400320000000000D12C0000000C0052006F00770020",
"00340033004600000000000000460000004500780061006D0070006C00650020",
"00540065007300740020004400650073006300720069007000740069006F006E",
"00200066006F007200200052006F00770020003400330000000000D12D000000",
"0C0052006F007700200034003400460000000000000046000000450078006100",
"6D0070006C006500200054006500730074002000440065007300630072006900",
"7000740069006F006E00200066006F007200200052006F007700200034003400",
"00000000D12E0000000C0052006F007700200034003500460000000000000046",
"0000004500780061006D0070006C006500200054006500730074002000440065",
"0073006300720069007000740069006F006E00200066006F007200200052006F",
"00770020003400350000000000D12F0000000C0052006F007700200034003600",
"4600000000000000460000004500780061006D0070006C006500200054006500",
"7300740020004400650073006300720069007000740069006F006E0020006600",
"6F007200200052006F00770020003400360000000000D1300000000C0052006F",
"0077002000340037004600000000000000460000004500780061006D0070006C",
"0065002000540065007300740020004400650073006300720069007000740069",
"006F006E00200066006F007200200052006F00770020003400370000000000D1",
"310000000C0052006F0077002000340038004600000000000000460000004500",
"780061006D0070006C0065002000540065007300740020004400650073006300",
"720069007000740069006F006E00200066006F007200200052006F0077002000",
"3400380000000000D1320000000C0052006F0077002000340039004600000000",
"000000460000004500780061006D0070006C0065002000540065007300740020",
"004400650073006300720069007000740069006F006E00200066006F00720020",
"0052006F00770020003400390000000000D1330000000C0052006F0077002000",
"350030004600000000000000460000004500780061006D0070006C0065002000",
"540065007300740020004400650073006300720069007000740069006F006E00",
"200066006F007200200052006F00770020003500300000000000D1340000000C",
"0052006F0077002000350031004600000000000000460000004500780061006D",
"0070006C00650020005400650073007400200044006500730063007200690070",
"00740069006F006E00200066006F007200200052006F00770020003500310000",
"000000D1350000000C0052006F00770020003500320046000000000000004600",
"00004500780061006D0070006C00650020005400650073007400200044006500",
"73006300720069007000740069006F006E00200066006F007200200052006F00",
"770020003500320000000000D1360000000C0052006F00770020003500330046",
"00000000000000460000004500780061006D0070006C00650020005400650073",
"00740020004400650073006300720069007000740069006F006E00200066006F",
"007200200052006F00770020003500330000000000D1370000000C0052006F00",
"77002000350034004600000000000000460000004500780061006D0070006C00",
"6500200054006500730074002000440065007300630072006900700074006900",
"6F006E00200066006F007200200052006F00770020003500340000000000D138",
"0000000C0052006F007700200035003500460000000000000046000000450078",
"0061006D0070006C006500200054006500730074002000440065007300630072",
"0069007000740069006F006E00200066006F007200200052006F007700200035",
"00350000000000D1390000000C0052006F007700200035003600460000000000",
"0000460000004500780061006D0070006C006500200054006500730074002000",
"4400650073006300720069007000740069006F006E00200066006F0072002000",
"52006F00770020003500360000000000D13A0000000C0052006F007700200035",
"0037004600000000000000460000004500780061006D0070006C006500200054",
"0065007300740020004400650073006300720069007000740069006F006E0020",
"0066006F007200200052006F00770020003500370000000000D13B0000000C00",
"52006F0077002000350038004600000000000000460000004500780061006D00",
"70006C0065002000540065007300740020004400650073006300720069007000",
"740069006F006E00200066006F007200200052006F0077002000350038000000",
"0000D13C0000000C0052006F0077002000350039004600000000000000460000",
"004500780061006D0070006C0065002000540065007300740020004400650073",
"006300720069007000740069006F006E00200066006F007200200052006F0077",
"0020003500390000000000D13D0000000C0052006F0077002000360030004600",
"000000000000460000004500780061006D0070006C0065002000540065007300",
"740020004400650073006300720069007000740069006F006E00200066006F00",
"7200200052006F00770020003600300000000000D13E0000000C0052006F0077",
"002000360031004600000000000000460000004500780061006D0070006C0065",
"002000540065007300740020004400650073006300720069007000740069006F",
"006E00200066006F007200200052006F00770020003600310000000000D13F00",
"00000C0052006F00770020003600320046000000000000004600000045007800",
"61006D0070006C00650020005400650073007400200044006500730063007200",
"69007000740069006F006E00200066006F007200200052006F00770020003600",
"320000000000D1400000000C0052006F00770020003600330046000000000000",
"00460000004500780061006D0070006C00650020005400650073007400200044",
"00650073006300720069007000740069006F006E00200066006F007200200052",
"006F00770020003600330000000000D1410000000C0052006F00770020003600",
"34004600000000000000460000004500780061006D0070006C00650020005400",
"65007300740020004400650073006300720069007000740069006F006E002000",
"66006F007200200052006F00770020003600340000000000D1420000000C0052",
"006F0077002000360035004600000000000000460000004500780061006D0070",
"006C006500200054006500730074002000440065007300630072006900700074",
"0069006F006E00200066006F007200200052006F007700200036003500000000",
"00D1430000000C0052006F007700200036003600460000000000000046000000",
"4500780061006D0070006C006500200054006500730074002000440065007300",
"6300720069007000740069006F006E00200066006F007200200052006F007700",
"20003600360000000000D1440000000C0052006F007700200036003700460000",
"0000000000460000004500780061006D0070006C006500200054006500730074",
"0020004400650073006300720069007000740069006F006E00200066006F0072",
"00200052006F00770020003600370000000000D1450000000C0052006F007700",
"2000360038004600000000000000460000004500780061006D0070006C006500",
"2000540065007300740020004400650073006300720069007000740069006F00",
"6E00200066006F007200200052006F00770020003600380000000000D1460000",
"000C0052006F0077002000360039004600000000000000460000004500780061",
"006D0070006C0065002000540065007300740020004400650073006300720069",
"007000740069006F006E00200066006F007200200052006F0077002000360039",
"0000000000D1470000000C0052006F0077002000370030004600000000000000",
"460000004500780061006D0070006C0065002000540065007300740020004400",
"650073006300720069007000740069006F006E00200066006F00720020005200",
"6F00770020003700300000000000D1480000000C0052006F0077002000370031",
"004600000000000000460000004500780061006D0070006C0065002000540065",
"007300740020004400650073006300720069007000740069006F006E00200066",
"006F007200200052006F00770020003700310000000000D1490000000C005200",
"6F0077002000370032004600000000000000460000004500780061006D007000",
"6C00650020005400650073007400200044006500730063007200690070007400",
"69006F006E00200066006F007200200052006F00770020003700320000000000",
"D14A0000000C0052006F00770020003700330046000000000000004600000045",
"00780061006D0070006C00650020005400650073007400200044006500730063",
"00720069007000740069006F006E00200066006F007200200052006F00770020",
"003700330000000000D14B0000000C0052006F00770020003700340046000000",
"00000000460000004500780061006D0070006C00650020005400650073007400",
"20004400650073006300720069007000740069006F006E00200066006F007200",
"200052006F00770020003700340000000000D14C0000000C0052006F00770020",
"00370035004600000000000000460000004500780061006D0070006C00650020",
"00540065007300740020004400650073006300720069007000740069006F006E",
"00200066006F007200200052006F00770020003700350000000000D14D000000",
"0C0052006F007700200037003600460000000000000046000000450078006100",
"6D0070006C006500200054006500730074002000440065007300630072006900",
"7000740069006F006E00200066006F007200200052006F007700200037003600",
"00000000D14E0000000C0052006F007700200037003700460000000000000015",
"0000004500780061006D0070006C00650020005400650073",
"3100000000740020004400650073006300720069007000740069006F006E0020",
"0066006F007200200052006F00770020003700370000000000D14F0000000C00",
"52006F0077002000370038004600000000000000460000004500780061006D00",
"70006C0065002000540065007300740020004400650073006300720069007000",
"740069006F006E00200066006F007200200052006F0077002000370038000000",
"0000D1500000000C0052006F0077002000370039004600000000000000460000",
"004500780061006D0070006C0065002000540065007300740020004400650073",
"006300720069007000740069006F006E00200066006F007200200052006F0077",
"0020003700390000000000D1510000000C0052006F0077002000380030004600",
"000000000000460000004500780061006D0070006C0065002000540065007300",
"740020004400650073006300720069007000740069006F006E00200066006F00",
"7200200052006F00770020003800300000000000D1520000000C0052006F0077",
"002000380031004600000000000000460000004500780061006D0070006C0065",
"002000540065007300740020004400650073006300720069007000740069006F",
"006E00200066006F007200200052006F00770020003800310000000000D15300",
"00000C0052006F00770020003800320046000000000000004600000045007800",
"61006D0070006C00650020005400650073007400200044006500730063007200",
"69007000740069006F006E00200066006F007200200052006F00770020003800",
"320000000000D1540000000C0052006F00770020003800330046000000000000",
"00460000004500780061006D0070006C00650020005400650073007400200044",
"00650073006300720069007000740069006F006E00200066006F007200200052",
"006F00770020003800330000000000D1550000000C0052006F00770020003800",
"34004600000000000000460000004500780061006D0070006C00650020005400",
"65007300740020004400650073006300720069007000740069006F006E002000",
"66006F007200200052006F00770020003800340000000000D1560000000C0052",
"006F0077002000380035004600000000000000460000004500780061006D0070",
"006C006500200054006500730074002000440065007300630072006900700074",
"0069006F006E00200066006F007200200052006F007700200038003500000000",
"00D1570000000C0052006F007700200038003600460000000000000046000000",
"4500780061006D0070006C006500200054006500730074002000440065007300",
"6300720069007000740069006F006E00200066006F007200200052006F007700",
"20003800360000000000D1580000000C0052006F007700200038003700460000",
"0000000000460000004500780061006D0070006C006500200054006500730074",
"0020004400650073006300720069007000740069006F006E00200066006F0072",
"00200052006F00770020003800370000000000D1590000000C0052006F007700",
"2000380038004600000000000000460000004500780061006D0070006C006500",
"2000540065007300740020004400650073006300720069007000740069006F00",
"6E00200066006F007200200052006F00770020003800380000000000D15A0000",
"000C0052006F0077002000380039004600000000000000460000004500780061",
"006D0070006C0065002000540065007300740020004400650073006300720069",
"007000740069006F006E00200066006F007200200052006F0077002000380039",
"0000000000D15B0000000C0052006F0077002000390030004600000000000000",
"460000004500780061006D0070006C0065002000540065007300740020004400",
"650073006300720069007000740069006F006E00200066006F00720020005200",
"6F00770020003900300000000000D15C0000000C0052006F0077002000390031",
"004600000000000000460000004500780061006D0070006C0065002000540065",
"007300740020004400650073006300720069007000740069006F006E00200066",
"006F007200200052006F00770020003900310000000000D15D0000000C005200",
"6F0077002000390032004600000000000000460000004500780061006D007000",
"6C00650020005400650073007400200044006500730063007200690070007400",
"69006F006E00200066006F007200200052006F00770020003900320000000000",
"D15E0000000C0052006F00770020003900330046000000000000004600000045",
"00780061006D0070006C00650020005400650073007400200044006500730063",
"00720069007000740069006F006E00200066006F007200200052006F00770020",
"003900330000000000D15F0000000C0052006F00770020003900340046000000",
"00000000460000004500780061006D0070006C00650020005400650073007400",
"20004400650073006300720069007000740069006F006E00200066006F007200",
"200052006F00770020003900340000000000D1600000000C0052006F00770020",
"00390035004600000000000000460000004500780061006D0070006C00650020",
"00540065007300740020004400650073006300720069007000740069006F006E",
"00200066006F007200200052006F00770020003900350000000000D161000000",
"0C0052006F007700200039003600460000000000000046000000450078006100",
"6D0070006C006500200054006500730074002000440065007300630072006900",
"7000740069006F006E00200066006F007200200052006F007700200039003600",
"00000000D1620000000C0052006F007700200039003700460000000000000046",
"0000004500780061006D0070006C006500200054006500730074002000440065",
"0073006300720069007000740069006F006E00200066006F007200200052006F",
"00770020003900370000000000D1630000000C0052006F007700200039003800",
"4600000000000000460000004500780061006D0070006C006500200054006500",
"7300740020004400650073006300720069007000740069006F006E0020006600",
"6F007200200052006F00770020003900380000000000D1640000000C0052006F",
"0077002000390039004600000000000000460000004500780061006D0070006C",
"0065002000540065007300740020004400650073006300720069007000740069",
"006F006E00200066006F007200200052006F00770020003900390000000000FF",
"1100C10064000000000000007900000000FE0000E0000000000000000000"
].join(""), "hex");
common.createBenchmark({
name: "parsing tokens for 100 rows",
profileIterations: 1000,
setup: function(cb) {
cb();
},
exec: function(cb) {
parser.on("doneProc", function() {
parser.removeAllListeners("doneProc");
cb();
});
parser.addBuffer(data);
},
teardown: function(cb) {
cb();
}
});
@@ -1,24 +0,0 @@
'use strict';
var TYPE = {
QUERY_NOTIFICATIONS: 1,
TXN_DESCRIPTOR: 2,
TRACE_ACTIVITY: 3
};
var TXNDESCRIPTOR_HEADER_DATA_LEN = 4 + 8;
var TXNDESCRIPTOR_HEADER_LEN = 4 + 2 + TXNDESCRIPTOR_HEADER_DATA_LEN;
module.exports.writeToTrackingBuffer = writeToTrackingBuffer;
function writeToTrackingBuffer(buffer, txnDescriptor, outstandingRequestCount) {
buffer.writeUInt32LE(0);
buffer.writeUInt32LE(TXNDESCRIPTOR_HEADER_LEN);
buffer.writeUInt16LE(TYPE.TXN_DESCRIPTOR);
buffer.writeBuffer(txnDescriptor);
buffer.writeUInt32LE(outstandingRequestCount);
var data = buffer.data;
data.writeUInt32LE(data.length, 0);
return buffer;
}
@@ -1,41 +0,0 @@
'use strict';
if (!Buffer.concat) {
Buffer.concat = function (buffers) {
var buffersCount = buffers.length;
var length = 0;
for (var i = 0; i < buffersCount; i++) {
var buffer = buffers[i];
length += buffer.length;
}
var result = new Buffer(length);
var position = 0;
for (var i = 0; i < buffersCount; i++) {
var buffer = buffers[i];
buffer.copy(result, position, 0);
position += buffer.length;
}
return result;
};
}
Buffer.prototype.toByteArray = function () {
return Array.prototype.slice.call(this, 0);
};
Buffer.prototype.equals = function (other) {
if (this.length !== other.length) {
return false;
}
for (var i = 0, len = this.length; i < len; i++) {
if (this[i] !== other[i]) {
return false;
}
}
return true;
};
@@ -1,236 +0,0 @@
'use strict';
var _get = require('babel-runtime/helpers/get')['default'];
var _inherits = require('babel-runtime/helpers/inherits')['default'];
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var EventEmitter = require('events').EventEmitter;
var WritableTrackingBuffer = require('./tracking-buffer/tracking-buffer').WritableTrackingBuffer;
var TOKEN_TYPE = require('./token/token').TYPE;
var FLAGS = {
nullable: 1 << 0,
caseSen: 1 << 1,
updateableReadWrite: 1 << 2,
updateableUnknown: 1 << 3,
identity: 1 << 4,
computed: 1 << 5, // introduced in TDS 7.2
fixedLenCLRType: 1 << 8, // introduced in TDS 7.2
sparseColumnSet: 1 << 10, // introduced in TDS 7.3.B
hidden: 1 << 13, // introduced in TDS 7.2
key: 1 << 14, // introduced in TDS 7.2
nullableUnknown: 1 << 15 // introduced in TDS 7.2
};
var DONE_STATUS = {
FINAL: 0x00,
MORE: 0x1,
ERROR: 0x2,
INXACT: 0x4,
COUNT: 0x10,
ATTN: 0x20,
SRVERROR: 0x100
};
module.exports = (function (_EventEmitter) {
_inherits(BulkLoad, _EventEmitter);
function BulkLoad(table, options1, callback) {
_classCallCheck(this, BulkLoad);
_get(Object.getPrototypeOf(BulkLoad.prototype), 'constructor', this).call(this);
this.error = undefined;
this.canceled = false;
this.table = table;
this.options = options1;
this.callback = callback;
this.columns = [];
this.columnsByName = {};
this.rowsData = new WritableTrackingBuffer(1024, 'ucs2', true);
this.firstRowWritten = false;
}
_createClass(BulkLoad, [{
key: 'addColumn',
value: function addColumn(name, type, options) {
if (options == null) {
options = {};
}
if (this.firstRowWritten) {
throw new Error('Columns cannot be added to bulk insert after the first row has been written.');
}
var column = {
type: type,
name: name,
value: null,
output: options.output || (options.output = false),
length: options.length,
precision: options.precision,
scale: options.scale,
objName: options.objName || name,
nullable: options.nullable
};
if ((type.id & 0x30) === 0x20) {
if (column.length == undefined && type.resolveLength) {
column.length = type.resolveLength(column);
}
}
if (type.hasPrecision) {
if (column.precision == undefined && type.resolvePrecision) {
column.precision = type.resolvePrecision(column);
}
}
if (type.hasScale) {
if (column.scale == undefined && type.resolveScale) {
column.scale = type.resolveScale(column);
}
}
this.columns.push(column);
return this.columnsByName[name] = column;
}
}, {
key: 'addRow',
value: function addRow(row) {
this.firstRowWritten = true;
if (arguments.length > 1 || !row || typeof row !== 'object') {
// convert arguments to array in a way the optimizer can handle
var arrTemp = new Array(arguments.length);
for (var i = 0, len = arguments.length; i < len; i++) {
var c = arguments[i];
arrTemp[i] = c;
}
row = arrTemp;
}
// write row token
this.rowsData.writeUInt8(TOKEN_TYPE.ROW);
// write each column
var arr = row instanceof Array;
for (var i = 0, len = this.columns.length; i < len; i++) {
var c = this.columns[i];
c.type.writeParameterData(this.rowsData, {
length: c.length,
scale: c.scale,
precision: c.precision,
value: row[arr ? i : c.objName]
}, this.options);
}
}
}, {
key: 'getBulkInsertSql',
value: function getBulkInsertSql() {
var sql = 'insert bulk ' + this.table + '(';
for (var i = 0, len = this.columns.length; i < len; i++) {
var c = this.columns[i];
if (i !== 0) {
sql += ', ';
}
sql += '[' + c.name + '] ' + c.type.declaration(c);
}
sql += ')';
return sql;
}
}, {
key: 'getTableCreationSql',
value: function getTableCreationSql() {
var sql = 'CREATE TABLE ' + this.table + '(\n';
for (var i = 0, len = this.columns.length; i < len; i++) {
var c = this.columns[i];
if (i !== 0) {
sql += ',\n';
}
sql += '[' + c.name + '] ' + c.type.declaration(c);
if (c.nullable !== void 0) {
sql += ' ' + (c.nullable ? 'NULL' : 'NOT NULL');
}
}
sql += '\n)';
return sql;
}
}, {
key: 'getPayload',
value: function getPayload() {
// Create COLMETADATA token
var metaData = this.getColMetaData();
var length = metaData.length;
// row data
var rows = this.rowsData.data;
length += rows.length;
// Create DONE token
// It might be nice to make DoneToken a class if anything needs to create them, but for now, just do it here
var tBuf = new WritableTrackingBuffer(this.options.tdsVersion < '7_2' ? 9 : 13);
tBuf.writeUInt8(TOKEN_TYPE.DONE);
var status = DONE_STATUS.FINAL;
tBuf.writeUInt16LE(status);
tBuf.writeUInt16LE(0); // CurCmd (TDS ignores this)
tBuf.writeUInt32LE(0); // row count - doesn't really matter
if (this.options.tdsVersion >= '7_2') {
tBuf.writeUInt32LE(0); // row count is 64 bits in >= TDS 7.2
}
var done = tBuf.data;
length += done.length;
// composite payload
var payload = new WritableTrackingBuffer(length);
payload.copyFrom(metaData);
payload.copyFrom(rows);
payload.copyFrom(done);
return payload;
}
}, {
key: 'getColMetaData',
value: function getColMetaData() {
var tBuf = new WritableTrackingBuffer(100, null, true);
// TokenType
tBuf.writeUInt8(TOKEN_TYPE.COLMETADATA);
// Count
tBuf.writeUInt16LE(this.columns.length);
for (var j = 0, len = this.columns.length; j < len; j++) {
var c = this.columns[j];
// UserType
if (this.options.tdsVersion < '7_2') {
tBuf.writeUInt16LE(0);
} else {
tBuf.writeUInt32LE(0);
}
// Flags
var flags = FLAGS.updateableReadWrite;
if (c.nullable) {
flags |= FLAGS.nullable;
} else if (c.nullable === void 0 && this.options.tdsVersion >= '7_2') {
flags |= FLAGS.nullableUnknown;
}
tBuf.writeUInt16LE(flags);
// TYPE_INFO
c.type.writeTypeInfo(tBuf, c, this.options);
// ColName
tBuf.writeBVarchar(c.name, 'ucs2');
}
return tBuf.data;
}
}]);
return BulkLoad;
})(EventEmitter);
@@ -1,186 +0,0 @@
'use strict';
// http://technet.microsoft.com/en-us/library/aa176553(v=sql.80).aspx
module.exports.codepageByLcid = {
0x436: 'CP1252',
0x401: 'CP1256',
0x801: 'CP1256',
0xC01: 'CP1256',
0x1001: 'CP1256',
0x1401: 'CP1256',
0x1801: 'CP1256',
0x1C01: 'CP1256',
0x2001: 'CP1256',
0x2401: 'CP1256',
0x2801: 'CP1256',
0x2C01: 'CP1256',
0x3001: 'CP1256',
0x3401: 'CP1256',
0x3801: 'CP1256',
0x3C01: 'CP1256',
0x4001: 'CP1256',
0x42D: 'CP1252',
0x423: 'CP1251',
0x402: 'CP1251',
0x403: 'CP1252',
0x30404: 'CP950',
0x404: 'CP950',
0x804: 'CP936',
0x20804: 'CP936',
0x1004: 'CP936',
0x41a: 'CP1250',
0x405: 'CP1250',
0x406: 'CP1252',
0x413: 'CP1252',
0x813: 'CP1252',
0x409: 'CP1252',
0x809: 'CP1252',
0x1009: 'CP1252',
0x1409: 'CP1252',
0xC09: 'CP1252',
0x1809: 'CP1252',
0x1C09: 'CP1252',
0x2409: 'CP1252',
0x2009: 'CP1252',
0x425: 'CP1257',
0x0438: 'CP1252',
0x429: 'CP1256',
0x40B: 'CP1252',
0x40C: 'CP1252',
0x80C: 'CP1252',
0x100C: 'CP1252',
0xC0C: 'CP1252',
0x140C: 'CP1252',
0x10437: 'CP1252',
0x10407: 'CP1252',
0x407: 'CP1252',
0x807: 'CP1252',
0xC07: 'CP1252',
0x1007: 'CP1252',
0x1407: 'CP1252',
0x408: 'CP1253',
0x40D: 'CP1255',
0x439: 'CPUTF8',
0x40E: 'CP1250',
0x104E: 'CP1250',
0x40F: 'CP1252',
0x421: 'CP1252',
0x410: 'CP1252',
0x810: 'CP1252',
0x411: 'CP932',
0x10411: 'CP932',
0x412: 'CP949',
0x426: 'CP1257',
0x427: 'CP1257',
0x827: 'CP1257',
0x41C: 'CP1251',
0x414: 'CP1252',
0x814: 'CP1252',
0x415: 'CP1250',
0x816: 'CP1252',
0x416: 'CP1252',
0x418: 'CP1250',
0x419: 'CP1251',
0x81A: 'CP1251',
0xC1A: 'CP1251',
0x41B: 'CP1250',
0x424: 'CP1250',
0x80A: 'CP1252',
0x40A: 'CP1252',
0xC0A: 'CP1252',
0x100A: 'CP1252',
0x140A: 'CP1252',
0x180A: 'CP1252',
0x1C0A: 'CP1252',
0x200A: 'CP1252',
0x240A: 'CP1252',
0x280A: 'CP1252',
0x2C0A: 'CP1252',
0x300A: 'CP1252',
0x340A: 'CP1252',
0x380A: 'CP1252',
0x3C0A: 'CP1252',
0x400A: 'CP1252',
0x41D: 'CP1252',
0x41E: 'CP874',
0x41F: 'CP1254',
0x422: 'CP1251',
0x420: 'CP1256',
0x42A: 'CP1258'
};
module.exports.codepageBySortId = {
30: 'CP437', // SQL_Latin1_General_CP437_BIN
31: 'CP437', // SQL_Latin1_General_CP437_CS_AS
32: 'CP437', // SQL_Latin1_General_CP437_CI_AS
33: 'CP437', // SQL_Latin1_General_Pref_CP437_CI_AS
34: 'CP437', // SQL_Latin1_General_CP437_CI_AI
40: 'CP850', // SQL_Latin1_General_CP850_BIN
41: 'CP850', // SQL_Latin1_General_CP850_CS_AS
42: 'CP850', // SQL_Latin1_General_CP850_CI_AS
43: 'CP850', // SQL_Latin1_General_Pref_CP850_CI_AS
44: 'CP850', // SQL_Latin1_General_CP850_CI_AI
49: 'CP850', // SQL_1xCompat_CP850_CI_AS
51: 'CP1252', // SQL_Latin1_General_Cp1_CS_AS_KI_WI
52: 'CP1252', // SQL_Latin1_General_Cp1_CI_AS_KI_WI
53: 'CP1252', // SQL_Latin1_General_Pref_Cp1_CI_AS_KI_WI
54: 'CP1252', // SQL_Latin1_General_Cp1_CI_AI_KI_WI
55: 'CP850', // SQL_AltDiction_CP850_CS_AS
56: 'CP850', // SQL_AltDiction_Pref_CP850_CI_AS
57: 'CP850', // SQL_AltDiction_CP850_CI_AI
58: 'CP850', // SQL_Scandinavian_Pref_CP850_CI_AS
59: 'CP850', // SQL_Scandinavian_CP850_CS_AS
60: 'CP850', // SQL_Scandinavian_CP850_CI_AS
61: 'CP850', // SQL_AltDiction_CP850_CI_AS
80: 'CP1250', // SQL_Latin1_General_1250_BIN
81: 'CP1250', // SQL_Latin1_General_CP1250_CS_AS
82: 'CP1250', // SQL_Latin1_General_Cp1250_CI_AS_KI_WI
83: 'CP1250', // SQL_Czech_Cp1250_CS_AS_KI_WI
84: 'CP1250', // SQL_Czech_Cp1250_CI_AS_KI_WI
85: 'CP1250', // SQL_Hungarian_Cp1250_CS_AS_KI_WI
86: 'CP1250', // SQL_Hungarian_Cp1250_CI_AS_KI_WI
87: 'CP1250', // SQL_Polish_Cp1250_CS_AS_KI_WI
88: 'CP1250', // SQL_Polish_Cp1250_CI_AS_KI_WI
89: 'CP1250', // SQL_Romanian_Cp1250_CS_AS_KI_WI
90: 'CP1250', // SQL_Romanian_Cp1250_CI_AS_KI_WI
91: 'CP1250', // SQL_Croatian_Cp1250_CS_AS_KI_WI
92: 'CP1250', // SQL_Croatian_Cp1250_CI_AS_KI_WI
93: 'CP1250', // SQL_Slovak_Cp1250_CS_AS_KI_WI
94: 'CP1250', // SQL_Slovak_Cp1250_CI_AS_KI_WI
95: 'CP1250', // SQL_Slovenian_Cp1250_CS_AS_KI_WI
96: 'CP1250', // SQL_Slovenian_Cp1250_CI_AS_KI_WI
104: 'CP1251', // SQL_Latin1_General_1251_BIN
105: 'CP1251', // SQL_Latin1_General_CP1251_CS_AS
106: 'CP1251', // SQL_Latin1_General_CP1251_CI_AS
107: 'CP1251', // SQL_Ukrainian_Cp1251_CS_AS_KI_WI
108: 'CP1251', // SQL_Ukrainian_Cp1251_CI_AS_KI_WI
112: 'CP1253', // SQL_Latin1_General_1253_BIN
113: 'CP1253', // SQL_Latin1_General_CP1253_CS_AS
114: 'CP1253', // SQL_Latin1_General_CP1253_CI_AS
120: 'CP1253', // SQL_MixDiction_CP1253_CS_AS
121: 'CP1253', // SQL_AltDiction_CP1253_CS_AS
122: 'CP1253', // SQL_AltDiction2_CP1253_CS_AS
124: 'CP1253', // SQL_Latin1_General_CP1253_CI_AI
128: 'CP1254', // SQL_Latin1_General_1254_BIN
129: 'CP1254', // SQL_Latin1_General_Cp1254_CS_AS_KI_WI
130: 'CP1254', // SQL_Latin1_General_Cp1254_CI_AS_KI_WI
136: 'CP1255', // SQL_Latin1_General_1255_BIN
137: 'CP1255', // SQL_Latin1_General_CP1255_CS_AS
138: 'CP1255', // SQL_Latin1_General_CP1255_CI_AS
144: 'CP1256', // SQL_Latin1_General_1256_BIN
145: 'CP1256', // SQL_Latin1_General_CP1256_CS_AS
146: 'CP1256', // SQL_Latin1_General_CP1256_CI_AS
152: 'CP1257', // SQL_Latin1_General_1257_BIN
153: 'CP1257', // SQL_Latin1_General_CP1257_CS_AS
154: 'CP1257', // SQL_Latin1_General_CP1257_CI_AS
155: 'CP1257', // SQL_Estonian_Cp1257_CS_AS_KI_WI
156: 'CP1257', // SQL_Estonian_Cp1257_CI_AS_KI_WI
157: 'CP1257', // SQL_Latvian_Cp1257_CS_AS_KI_WI
158: 'CP1257', // SQL_Latvian_Cp1257_CI_AS_KI_WI
159: 'CP1257', // SQL_Lithuanian_Cp1257_CS_AS_KI_WI
160: 'CP1257', // SQL_Lithuanian_Cp1257_CI_AS_KI_WI
183: 'CP1252', // SQL_Danish_Pref_Cp1_CI_AS_KI_WI
184: 'CP1252', // SQL_SwedishPhone_Pref_Cp1_CI_AS_KI_WI
185: 'CP1252', // SQL_SwedishStd_Pref_Cp1_CI_AS_KI_WI
186: 'CP1252' // SQL_Icelandic_Pref_Cp1_CI_AS_KI_WI
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,80 +0,0 @@
'use strict';
var _get = require('babel-runtime/helpers/get')['default'];
var _inherits = require('babel-runtime/helpers/inherits')['default'];
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var EventEmitter = require('events').EventEmitter;
var util = require('util');
module.exports = (function (_EventEmitter) {
_inherits(Debug, _EventEmitter);
/*
@options Which debug details should be sent.
data - dump of packet data
payload - details of decoded payload
*/
function Debug(options) {
_classCallCheck(this, Debug);
_get(Object.getPrototypeOf(Debug.prototype), 'constructor', this).call(this);
this.options = options;
this.options = this.options || {};
this.options.data = this.options.data || false;
this.options.payload = this.options.payload || false;
this.options.packet = this.options.packet || false;
this.options.token = this.options.token || false;
this.indent = ' ';
}
_createClass(Debug, [{
key: 'packet',
value: function packet(direction, _packet) {
if (this.haveListeners() && this.options.packet) {
this.log('');
this.log(direction);
this.log(_packet.headerToString(this.indent));
}
}
}, {
key: 'data',
value: function data(packet) {
if (this.haveListeners() && this.options.data) {
this.log(packet.dataToString(this.indent));
}
}
}, {
key: 'payload',
value: function payload(generatePayloadText) {
if (this.haveListeners() && this.options.payload) {
this.log(generatePayloadText());
}
}
}, {
key: 'token',
value: function token(_token) {
if (this.haveListeners() && this.options.token) {
this.log(util.inspect(_token, false, 5, true));
}
}
}, {
key: 'haveListeners',
value: function haveListeners() {
return this.listeners('debug').length > 0;
}
}, {
key: 'log',
value: function log(text) {
this.emit('debug', text);
}
}]);
return Debug;
})(EventEmitter);
@@ -1,47 +0,0 @@
'use strict';
var util = require('util');
module.exports.ConnectionError = ConnectionError;
function ConnectionError(message, code) {
if (!(this instanceof ConnectionError)) {
if (message instanceof ConnectionError) {
return message;
}
return new ConnectionError(message, code);
}
Error.call(this);
this.message = message;
this.code = code;
Error.captureStackTrace(this, this.constructor);
}
util.inherits(ConnectionError, Error);
ConnectionError.prototype.name = 'ConnectionError';
module.exports.RequestError = RequestError;
function RequestError(message, code) {
if (!(this instanceof RequestError)) {
if (message instanceof RequestError) {
return message;
}
return new RequestError(message, code);
}
Error.call(this);
this.message = message;
this.code = code;
Error.captureStackTrace(this, this.constructor);
}
util.inherits(RequestError, Error);
RequestError.prototype.name = 'RequestError';
@@ -1,19 +0,0 @@
'use strict';
function formatHex(number) {
var hex = number.toString(16);
if (hex.length === 1) {
hex = '0' + hex;
}
return hex;
}
module.exports.arrayToGuid = arrayToGuid;
function arrayToGuid(array) {
return (formatHex(array[3]) + formatHex(array[2]) + formatHex(array[1]) + formatHex(array[0]) + '-' + formatHex(array[5]) + formatHex(array[4]) + '-' + formatHex(array[7]) + formatHex(array[6]) + '-' + formatHex(array[8]) + formatHex(array[9]) + '-' + formatHex(array[10]) + formatHex(array[11]) + formatHex(array[12]) + formatHex(array[13]) + formatHex(array[14]) + formatHex(array[15])).toUpperCase();
}
module.exports.guidToArray = guidToArray;
function guidToArray(guid) {
return [parseInt(guid.substring(6, 8), 16), parseInt(guid.substring(4, 6), 16), parseInt(guid.substring(2, 4), 16), parseInt(guid.substring(0, 2), 16), parseInt(guid.substring(11, 13), 16), parseInt(guid.substring(9, 11), 16), parseInt(guid.substring(16, 18), 16), parseInt(guid.substring(14, 16), 16), parseInt(guid.substring(19, 21), 16), parseInt(guid.substring(21, 23), 16), parseInt(guid.substring(24, 26), 16), parseInt(guid.substring(26, 28), 16), parseInt(guid.substring(28, 30), 16), parseInt(guid.substring(30, 32), 16), parseInt(guid.substring(32, 34), 16), parseInt(guid.substring(34, 36), 16)];
}
@@ -1,93 +0,0 @@
'use strict';
var dgram = require('dgram');
var SQL_SERVER_BROWSER_PORT = 1434;
var TIMEOUT = 2 * 1000;
var RETRIES = 3;
// There are three bytes at the start of the response, whose purpose is unknown.
var MYSTERY_HEADER_LENGTH = 3;
// Most of the functionality has been determined from from jTDS's MSSqlServerInfo class.
module.exports.instanceLookup = instanceLookup;
function instanceLookup(server, instanceName, callback, timeout, retries) {
var socket = undefined,
timer = undefined;
timeout = timeout || TIMEOUT;
var retriesLeft = retries || RETRIES;
function onMessage(message) {
if (timer) {
clearTimeout(timer);
timer = undefined;
}
message = message.toString('ascii', MYSTERY_HEADER_LENGTH);
var port = parseBrowserResponse(message, instanceName);
socket.close();
if (port) {
return callback(undefined, port);
} else {
return callback('Port for ' + instanceName + ' not found in ' + message);
}
}
function onError(err) {
if (timer) {
clearTimeout(timer);
timer = undefined;
}
socket.close();
return callback('Failed to lookup instance on ' + server + ' - ' + err.message);
}
function onTimeout() {
timer = undefined;
socket.close();
return makeAttempt();
}
function makeAttempt() {
if (retriesLeft > 0) {
retriesLeft--;
var request = new Buffer([0x02]);
socket = dgram.createSocket('udp4');
socket.on('error', onError);
socket.on('message', onMessage);
socket.send(request, 0, request.length, SQL_SERVER_BROWSER_PORT, server);
return timer = setTimeout(onTimeout, timeout);
} else {
return callback('Failed to get response from SQL Server Browser on ' + server);
}
}
return makeAttempt();
}
module.exports.parseBrowserResponse = parseBrowserResponse;
function parseBrowserResponse(response, instanceName) {
var getPort = undefined;
var instances = response.split(';;');
for (var i = 0, len = instances.length; i < len; i++) {
var instance = instances[i];
var parts = instance.split(';');
for (var p = 0, partsLen = parts.length; p < partsLen; p += 2) {
var _name = parts[p];
var value = parts[p + 1];
if (_name === 'tcp' && getPort) {
var port = parseInt(value, 10);
return port;
}
if (_name === 'InstanceName') {
if (value.toUpperCase() === instanceName.toUpperCase()) {
getPort = true;
} else {
getPort = false;
}
}
}
}
}
@@ -1,3 +0,0 @@
'use strict';
module.exports.name = 'Tedious';
@@ -1,295 +0,0 @@
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
require('./buffertools');
var WritableTrackingBuffer = require('./tracking-buffer/writable-tracking-buffer');
var os = require('os');
var sprintf = require('sprintf').sprintf;
var libraryName = require('./library').name;
var versions = require('./tds-versions').versions;
var FLAGS_1 = {
ENDIAN_LITTLE: 0x00,
ENDIAN_BIG: 0x01,
CHARSET_ASCII: 0x00,
CHARSET_EBCDIC: 0x02,
FLOAT_IEEE_754: 0x00,
FLOAT_VAX: 0x04,
FLOAT_ND5000: 0x08,
BCP_DUMPLOAD_ON: 0x00,
BCP_DUMPLOAD_OFF: 0x10,
USE_DB_ON: 0x00,
USE_DB_OFF: 0x20,
INIT_DB_WARN: 0x00,
INIT_DB_FATAL: 0x40,
SET_LANG_WARN_OFF: 0x00,
SET_LANG_WARN_ON: 0x80
};
var FLAGS_2 = {
INIT_LANG_WARN: 0x00,
INIT_LANG_FATAL: 0x01,
ODBC_OFF: 0x00,
ODBC_ON: 0x02,
F_TRAN_BOUNDARY: 0x04,
F_CACHE_CONNECT: 0x08,
USER_NORMAL: 0x00,
USER_SERVER: 0x10,
USER_REMUSER: 0x20,
USER_SQLREPL: 0x40,
INTEGRATED_SECURITY_OFF: 0x00,
INTEGRATED_SECURITY_ON: 0x80
};
var TYPE_FLAGS = {
SQL_DFLT: 0x00,
SQL_TSQL: 0x08,
OLEDB_OFF: 0x00,
OLEDB_ON: 0x10,
READ_WRITE_INTENT: 0x00,
READ_ONLY_INTENT: 0x20
};
var FLAGS_3 = {
CHANGE_PASSWORD_NO: 0x00,
CHANGE_PASSWORD_YES: 0x01,
BINARY_XML: 0x02,
SPAWN_USER_INSTANCE: 0x04,
UNKNOWN_COLLATION_HANDLING: 0x08
};
var NTLMFlags = {
NTLM_NegotiateUnicode: 0x00000001,
NTLM_NegotiateOEM: 0x00000002,
NTLM_RequestTarget: 0x00000004,
NTLM_Unknown9: 0x00000008,
NTLM_NegotiateSign: 0x00000010,
NTLM_NegotiateSeal: 0x00000020,
NTLM_NegotiateDatagram: 0x00000040,
NTLM_NegotiateLanManagerKey: 0x00000080,
NTLM_Unknown8: 0x00000100,
NTLM_NegotiateNTLM: 0x00000200,
NTLM_NegotiateNTOnly: 0x00000400,
NTLM_Anonymous: 0x00000800,
NTLM_NegotiateOemDomainSupplied: 0x00001000,
NTLM_NegotiateOemWorkstationSupplied: 0x00002000,
NTLM_Unknown6: 0x00004000,
NTLM_NegotiateAlwaysSign: 0x00008000,
NTLM_TargetTypeDomain: 0x00010000,
NTLM_TargetTypeServer: 0x00020000,
NTLM_TargetTypeShare: 0x00040000,
NTLM_NegotiateExtendedSecurity: 0x00080000,
NTLM_NegotiateIdentify: 0x00100000,
NTLM_Unknown5: 0x00200000,
NTLM_RequestNonNTSessionKey: 0x00400000,
NTLM_NegotiateTargetInfo: 0x00800000,
NTLM_Unknown4: 0x01000000,
NTLM_NegotiateVersion: 0x02000000,
NTLM_Unknown3: 0x04000000,
NTLM_Unknown2: 0x08000000,
NTLM_Unknown1: 0x10000000,
NTLM_Negotiate128: 0x20000000,
NTLM_NegotiateKeyExchange: 0x40000000,
NTLM_Negotiate56: 0x80000000
};
/*
s2.2.6.3
*/
module.exports = (function () {
function Login7Payload(loginData) {
_classCallCheck(this, Login7Payload);
this.loginData = loginData;
var lengthLength = 4;
var fixed = this.createFixedData();
var variable = this.createVariableData(lengthLength + fixed.length);
var length = lengthLength + fixed.length + variable.length;
var data = new WritableTrackingBuffer(300);
data.writeUInt32LE(length);
data.writeBuffer(fixed);
data.writeBuffer(variable);
this.data = data.data;
}
_createClass(Login7Payload, [{
key: 'createFixedData',
value: function createFixedData() {
this.tdsVersion = versions[this.loginData.tdsVersion];
this.packetSize = this.loginData.packetSize;
this.clientProgVer = 0;
this.clientPid = process.pid;
this.connectionId = 0;
this.clientTimeZone = new Date().getTimezoneOffset();
this.clientLcid = 0x00000409;
this.flags1 = FLAGS_1.ENDIAN_LITTLE | FLAGS_1.CHARSET_ASCII | FLAGS_1.FLOAT_IEEE_754 | FLAGS_1.BCD_DUMPLOAD_OFF | FLAGS_1.USE_DB_OFF | FLAGS_1.SET_LANG_WARN_ON;
if (this.loginData.initDbFatal) {
this.flags1 |= FLAGS_1.INIT_DB_FATAL;
} else {
this.flags1 |= FLAGS_1.INIT_DB_WARN;
}
this.flags2 = FLAGS_2.INIT_LANG_WARN | FLAGS_2.ODBC_OFF | FLAGS_2.USER_NORMAL;
if (this.loginData.domain) {
this.flags2 |= FLAGS_2.INTEGRATED_SECURITY_ON;
} else {
this.flags2 |= FLAGS_2.INTEGRATED_SECURITY_OFF;
}
this.flags3 = FLAGS_3.CHANGE_PASSWORD_NO | FLAGS_3.UNKNOWN_COLLATION_HANDLING;
this.typeFlags = TYPE_FLAGS.SQL_DFLT | TYPE_FLAGS.OLEDB_OFF;
if (this.loginData.readOnlyIntent) {
this.typeFlags |= TYPE_FLAGS.READ_ONLY_INTENT;
} else {
this.typeFlags |= TYPE_FLAGS.READ_WRITE_INTENT;
}
var buffer = new WritableTrackingBuffer(100);
buffer.writeUInt32LE(this.tdsVersion);
buffer.writeUInt32LE(this.packetSize);
buffer.writeUInt32LE(this.clientProgVer);
buffer.writeUInt32LE(this.clientPid);
buffer.writeUInt32LE(this.connectionId);
buffer.writeUInt8(this.flags1);
buffer.writeUInt8(this.flags2);
buffer.writeUInt8(this.typeFlags);
buffer.writeUInt8(this.flags3);
buffer.writeInt32LE(this.clientTimeZone);
buffer.writeUInt32LE(this.clientLcid);
return buffer.data;
}
}, {
key: 'createVariableData',
value: function createVariableData(offset) {
this.variableLengthsLength = 9 * 4 + 6 + 3 * 4 + 4;
if (this.loginData.tdsVersion === '7_1') {
this.variableLengthsLength = 9 * 4 + 6 + 2 * 4;
}
var variableData = {
offsetsAndLengths: new WritableTrackingBuffer(200),
data: new WritableTrackingBuffer(200, 'ucs2'),
offset: offset + this.variableLengthsLength
};
this.hostname = os.hostname();
this.loginData = this.loginData || {};
this.loginData.appName = this.loginData.appName || 'Tedious';
this.libraryName = libraryName;
this.clientId = new Buffer([1, 2, 3, 4, 5, 6]);
if (!this.loginData.domain) {
this.sspi = '';
this.sspiLong = 0;
}
this.attachDbFile = '';
this.changePassword = '';
this.addVariableDataString(variableData, this.hostname);
this.addVariableDataString(variableData, this.loginData.userName);
this.addVariableDataBuffer(variableData, this.createPasswordBuffer());
this.addVariableDataString(variableData, this.loginData.appName);
this.addVariableDataString(variableData, this.loginData.serverName);
this.addVariableDataString(variableData, '');
this.addVariableDataString(variableData, this.libraryName);
this.addVariableDataString(variableData, this.loginData.language);
this.addVariableDataString(variableData, this.loginData.database);
variableData.offsetsAndLengths.writeBuffer(this.clientId);
if (this.loginData.domain) {
this.ntlmPacket = this.createNTLMRequest(this.loginData);
this.sspiLong = this.ntlmPacket.length;
variableData.offsetsAndLengths.writeUInt16LE(variableData.offset);
variableData.offsetsAndLengths.writeUInt16LE(this.ntlmPacket.length);
variableData.data.writeBuffer(this.ntlmPacket);
variableData.offset += this.ntlmPacket.length;
} else {
this.addVariableDataString(variableData, this.sspi);
}
this.addVariableDataString(variableData, this.attachDbFile);
if (this.loginData.tdsVersion > '7_1') {
this.addVariableDataString(variableData, this.changePassword);
variableData.offsetsAndLengths.writeUInt32LE(this.sspiLong);
}
return Buffer.concat([variableData.offsetsAndLengths.data, variableData.data.data]);
}
}, {
key: 'addVariableDataBuffer',
value: function addVariableDataBuffer(variableData, buffer) {
variableData.offsetsAndLengths.writeUInt16LE(variableData.offset);
variableData.offsetsAndLengths.writeUInt16LE(buffer.length / 2);
variableData.data.writeBuffer(buffer);
return variableData.offset += buffer.length;
}
}, {
key: 'addVariableDataString',
value: function addVariableDataString(variableData, value) {
value || (value = '');
variableData.offsetsAndLengths.writeUInt16LE(variableData.offset);
variableData.offsetsAndLengths.writeUInt16LE(value.length);
variableData.data.writeString(value);
return variableData.offset += value.length * 2;
}
}, {
key: 'createNTLMRequest',
value: function createNTLMRequest(options) {
var domain = escape(options.domain.toUpperCase());
var workstation = options.workstation ? escape(options.workstation.toUpperCase()) : '';
var protocol = 'NTLMSSP\u0000';
var BODY_LENGTH = 40;
var bufferLength = BODY_LENGTH + domain.length;
var buffer = new WritableTrackingBuffer(bufferLength);
var type1flags = this.getNTLMFlags();
if (workstation === '') {
type1flags -= NTLMFlags.NTLM_NegotiateOemWorkstationSupplied;
}
buffer.writeString(protocol, 'utf8');
buffer.writeUInt32LE(1);
buffer.writeUInt32LE(type1flags);
buffer.writeUInt16LE(domain.length);
buffer.writeUInt16LE(domain.length);
buffer.writeUInt32LE(BODY_LENGTH + workstation.length);
buffer.writeUInt16LE(workstation.length);
buffer.writeUInt16LE(workstation.length);
buffer.writeUInt32LE(BODY_LENGTH);
buffer.writeUInt8(5);
buffer.writeUInt8(0);
buffer.writeUInt16LE(2195);
buffer.writeUInt8(0);
buffer.writeUInt8(0);
buffer.writeUInt8(0);
buffer.writeUInt8(15);
buffer.writeString(workstation, 'ascii');
buffer.writeString(domain, 'ascii');
return buffer.data;
}
}, {
key: 'createPasswordBuffer',
value: function createPasswordBuffer() {
var password = this.loginData.password || '';
password = new Buffer(password, 'ucs2');
for (var b = 0, len = password.length; b < len; b++) {
var byte = password[b];
var lowNibble = byte & 0x0f;
var highNibble = byte >> 4;
byte = lowNibble << 4 | highNibble;
byte = byte ^ 0xa5;
password[b] = byte;
}
return password;
}
}, {
key: 'getNTLMFlags',
value: function getNTLMFlags() {
return NTLMFlags.NTLM_NegotiateUnicode + NTLMFlags.NTLM_NegotiateOEM + NTLMFlags.NTLM_RequestTarget + NTLMFlags.NTLM_NegotiateNTLM + NTLMFlags.NTLM_NegotiateOemDomainSupplied + NTLMFlags.NTLM_NegotiateOemWorkstationSupplied + NTLMFlags.NTLM_NegotiateAlwaysSign + NTLMFlags.NTLM_NegotiateVersion + NTLMFlags.NTLM_NegotiateExtendedSecurity + NTLMFlags.NTLM_Negotiate128 + NTLMFlags.NTLM_Negotiate56;
}
}, {
key: 'toString',
value: function toString(indent) {
indent || (indent = '');
return indent + 'Login7 - ' + sprintf('TDS:0x%08X, PacketSize:0x%08X, ClientProgVer:0x%08X, ClientPID:0x%08X, ConnectionID:0x%08X', this.tdsVersion, this.packetSize, this.clientProgVer, this.clientPid, this.connectionId) + '\n' + indent + ' ' + sprintf('Flags1:0x%02X, Flags2:0x%02X, TypeFlags:0x%02X, Flags3:0x%02X, ClientTimezone:%d, ClientLCID:0x%08X', this.flags1, this.flags2, this.typeFlags, this.flags3, this.clientTimeZone, this.clientLcid) + '\n' + indent + ' ' + sprintf("Hostname:'%s', Username:'%s', Password:'%s', AppName:'%s', ServerName:'%s', LibraryName:'%s'", this.hostname, this.loginData.userName, this.loginData.password, this.loginData.appName, this.loginData.serverName, libraryName) + '\n' + indent + ' ' + sprintf("Language:'%s', Database:'%s', SSPI:'%s', AttachDbFile:'%s', ChangePassword:'%s'", this.loginData.language, this.loginData.database, this.sspi, this.attachDbFile, this.changePassword);
}
}]);
return Login7Payload;
})();
@@ -1,205 +0,0 @@
'use strict';
var _get = require('babel-runtime/helpers/get')['default'];
var _inherits = require('babel-runtime/helpers/inherits')['default'];
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var tls = require('tls');
var crypto = require('crypto');
var EventEmitter = require('events').EventEmitter;
var Transform = require('readable-stream').Transform;
require('./buffertools');
var Packet = require('./packet').Packet;
var TYPE = require('./packet').TYPE;
var packetHeaderLength = require('./packet').HEADER_LENGTH;
var ReadablePacketStream = (function (_Transform) {
_inherits(ReadablePacketStream, _Transform);
function ReadablePacketStream() {
_classCallCheck(this, ReadablePacketStream);
_get(Object.getPrototypeOf(ReadablePacketStream.prototype), 'constructor', this).call(this, { objectMode: true });
this.buffer = new Buffer(0);
this.position = 0;
}
_createClass(ReadablePacketStream, [{
key: '_transform',
value: function _transform(chunk, encoding, callback) {
if (this.position === this.buffer.length) {
// If we have fully consumed the previous buffer,
// we can just replace it with the new chunk
this.buffer = chunk;
} else {
// If we haven't fully consumed the previous buffer,
// we simply concatenate the leftovers and the new chunk.
this.buffer = Buffer.concat([this.buffer.slice(this.position), chunk], this.buffer.length - this.position + chunk.length);
}
this.position = 0;
// The packet header is always 8 bytes of length.
while (this.buffer.length >= this.position + packetHeaderLength) {
// Get the full packet length
var _length = this.buffer.readUInt16BE(this.position + 2);
if (this.buffer.length >= this.position + _length) {
var data = this.buffer.slice(this.position, this.position + _length);
this.position += _length;
this.push(new Packet(data));
} else {
// Not enough data to provide the next packet. Stop here and wait for
// the next call to `_transform`.
break;
}
}
callback();
}
}]);
return ReadablePacketStream;
})(Transform);
module.exports = (function (_EventEmitter) {
_inherits(MessageIO, _EventEmitter);
function MessageIO(socket, _packetSize, debug) {
var _this = this;
_classCallCheck(this, MessageIO);
_get(Object.getPrototypeOf(MessageIO.prototype), 'constructor', this).call(this);
this.socket = socket;
this._packetSize = _packetSize;
this.debug = debug;
this.sendPacket = this.sendPacket.bind(this);
this.packetStream = new ReadablePacketStream();
this.packetStream.on('data', function (packet) {
_this.logPacket('Received', packet);
_this.emit('data', packet.data());
if (packet.isLast()) {
_this.emit('message');
}
});
this.socket.pipe(this.packetStream);
this.packetDataSize = this._packetSize - packetHeaderLength;
}
_createClass(MessageIO, [{
key: 'packetSize',
value: function packetSize(_packetSize2) {
if (arguments.length > 0) {
this.debug.log('Packet size changed from ' + this._packetSize + ' to ' + _packetSize2);
this._packetSize = _packetSize2;
this.packetDataSize = this._packetSize - packetHeaderLength;
}
return this._packetSize;
}
}, {
key: 'startTls',
value: function startTls(credentialsDetails) {
var _this2 = this;
var credentials = tls.createSecureContext ? tls.createSecureContext(credentialsDetails) : crypto.createCredentials(credentialsDetails);
this.securePair = tls.createSecurePair(credentials);
this.tlsNegotiationComplete = false;
this.securePair.on('secure', function () {
var cipher = _this2.securePair.cleartext.getCipher();
_this2.debug.log('TLS negotiated (' + cipher.name + ', ' + cipher.version + ')');
_this2.emit('secure', _this2.securePair.cleartext);
_this2.encryptAllFutureTraffic();
});
this.securePair.encrypted.on('data', function (data) {
_this2.sendMessage(TYPE.PRELOGIN, data);
});
// On Node >= 0.12, the encrypted stream automatically starts spewing out
// data once we attach a `data` listener. But on Node <= 0.10.x, this is not
// the case. We need to kick the cleartext stream once to get the
// encrypted end of the secure pair to emit the TLS handshake data.
this.securePair.cleartext.write('');
}
}, {
key: 'encryptAllFutureTraffic',
value: function encryptAllFutureTraffic() {
this.socket.unpipe(this.packetStream);
this.securePair.encrypted.removeAllListeners('data');
this.socket.pipe(this.securePair.encrypted);
this.securePair.encrypted.pipe(this.socket);
this.securePair.cleartext.pipe(this.packetStream);
this.tlsNegotiationComplete = true;
}
}, {
key: 'tlsHandshakeData',
value: function tlsHandshakeData(data) {
this.securePair.encrypted.write(data);
}
// TODO listen for 'drain' event when socket.write returns false.
// TODO implement incomplete request cancelation (2.2.1.6)
}, {
key: 'sendMessage',
value: function sendMessage(packetType, data, resetConnection) {
var numberOfPackets = undefined;
if (data) {
numberOfPackets = Math.floor((data.length - 1) / this.packetDataSize) + 1;
} else {
numberOfPackets = 1;
data = new Buffer(0);
}
for (var packetNumber = 0; packetNumber < numberOfPackets; packetNumber++) {
var payloadStart = packetNumber * this.packetDataSize;
var payloadEnd = undefined;
if (packetNumber < numberOfPackets - 1) {
payloadEnd = payloadStart + this.packetDataSize;
} else {
payloadEnd = data.length;
}
var packetPayload = data.slice(payloadStart, payloadEnd);
var packet = new Packet(packetType);
packet.last(packetNumber === numberOfPackets - 1);
packet.resetConnection(resetConnection);
packet.packetId(packetNumber + 1);
packet.addData(packetPayload);
this.sendPacket(packet);
}
}
}, {
key: 'sendPacket',
value: function sendPacket(packet) {
this.logPacket('Sent', packet);
if (this.securePair && this.tlsNegotiationComplete) {
this.securePair.cleartext.write(packet.buffer);
} else {
this.socket.write(packet.buffer);
}
}
}, {
key: 'logPacket',
value: function logPacket(direction, packet) {
this.debug.packet(direction, packet);
return this.debug.data(packet);
}
}]);
return MessageIO;
})(EventEmitter);
@@ -1,177 +0,0 @@
'use strict';
var codepageBySortId = require('./collation').codepageBySortId;
var codepageByLcid = require('./collation').codepageByLcid;
var TYPE = require('./data-type').TYPE;
var sprintf = require('sprintf').sprintf;
module.exports = metadataParse;
module.exports.readPrecision = readPrecision;
module.exports.readScale = readScale;
module.exports.readCollation = readCollation;
function readDataLength(parser, type, callback) {
if ((type.id & 0x30) === 0x20) {
// xx10xxxx - s2.2.4.2.1.3
// Variable length
if (type.dataLengthFromScale) {
return callback(0); // dataLength is resolved from scale
} else if (type.fixedDataLength) {
return callback(type.fixedDataLength);
}
switch (type.dataLengthLength) {
case 0:
return callback(undefined);
case 1:
return parser.readUInt8(callback);
case 2:
return parser.readUInt16LE(callback);
case 4:
return parser.readUInt32LE(callback);
default:
return parser.emit(new Error('Unsupported dataLengthLength ' + type.dataLengthLength + ' for data type ' + type.name));
}
} else {
callback(undefined);
}
}
function readPrecision(parser, type, callback) {
if (type.hasPrecision) {
parser.readUInt8(callback);
} else {
callback(undefined);
}
}
function readScale(parser, type, callback) {
if (type.hasScale) {
parser.readUInt8(callback);
} else {
callback(undefined);
}
}
function readCollation(parser, type, callback) {
if (type.hasCollation) {
// s2.2.5.1.2
parser.readBuffer(5, function (collationData) {
var collation = {};
collation.lcid = (collationData[2] & 0x0F) << 16;
collation.lcid |= collationData[1] << 8;
collation.lcid |= collationData[0];
// This may not be extracting the correct nibbles in the correct order.
collation.flags = collationData[3] >> 4;
collation.flags |= collationData[2] & 0xF0;
// This may not be extracting the correct nibble.
collation.version = collationData[3] & 0x0F;
collation.sortId = collationData[4];
collation.codepage = codepageBySortId[collation.sortId] || codepageByLcid[collation.lcid] || 'CP1252';
callback(collation);
});
} else {
callback(undefined);
}
}
function readSchema(parser, type, callback) {
if (type.hasSchemaPresent) {
// s2.2.5.5.3
parser.readUInt8(function (schemaPresent) {
if (schemaPresent === 0x01) {
parser.readBVarChar(function (dbname) {
parser.readBVarChar(function (owningSchema) {
parser.readUsVarChar(function (xmlSchemaCollection) {
callback({
dbname: dbname,
owningSchema: owningSchema,
xmlSchemaCollection: xmlSchemaCollection
});
});
});
});
} else {
callback(undefined);
}
});
} else {
callback(undefined);
}
}
function readUDTInfo(parser, type, callback) {
if (type.hasUDTInfo) {
parser.readUInt16LE(function (maxByteSize) {
parser.readBVarChar(function (dbname) {
parser.readBVarChar(function (owningSchema) {
parser.readBVarChar(function (typeName) {
parser.readUsVarChar(function (assemblyName) {
callback({
maxByteSize: maxByteSize,
dbname: dbname,
owningSchema: owningSchema,
typeName: typeName,
assemblyName: assemblyName
});
});
});
});
});
});
} else {
return callback();
}
}
function metadataParse(parser, options, callback) {
(options.tdsVersion < '7_2' ? parser.readUInt16LE : parser.readUInt32LE).call(parser, function (userType) {
parser.readUInt16LE(function (flags) {
parser.readUInt8(function (typeNumber) {
var type = TYPE[typeNumber];
if (!type) {
return parser.emit(new Error(sprintf('Unrecognised data type 0x%02X', typeNumber)));
}
readDataLength(parser, type, function (dataLength) {
readPrecision(parser, type, function (precision) {
readScale(parser, type, function (scale) {
if (scale && type.dataLengthFromScale) {
dataLength = type.dataLengthFromScale(scale);
}
readCollation(parser, type, function (collation) {
readSchema(parser, type, function (schema) {
readUDTInfo(parser, type, function (udtInfo) {
callback({
userType: userType,
flags: flags,
type: type,
collation: collation,
precision: precision,
scale: scale,
dataLength: dataLength,
schema: schema,
udtInfo: udtInfo
});
});
});
});
});
});
});
});
});
});
}
@@ -1,190 +0,0 @@
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var WritableTrackingBuffer = require('./tracking-buffer/writable-tracking-buffer');
var crypto = require('crypto');
var BigInteger = require('big-number').n;
var hex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
module.exports = (function () {
function NTLMResponsePayload(loginData) {
_classCallCheck(this, NTLMResponsePayload);
this.data = this.createResponse(loginData);
}
_createClass(NTLMResponsePayload, [{
key: 'toString',
value: function toString(indent) {
indent || (indent = '');
return indent + 'NTLM Auth';
}
}, {
key: 'createResponse',
value: function createResponse(challenge) {
var client_nonce = this.createClientNonce();
var lmv2len = 24;
var ntlmv2len = 16;
var domain = challenge.domain;
var username = challenge.userName;
var password = challenge.password;
var ntlmData = challenge.ntlmpacket;
var server_data = ntlmData.target;
var server_nonce = ntlmData.nonce;
var bufferLength = 64 + domain.length * 2 + username.length * 2 + lmv2len + ntlmv2len + 8 + 8 + 8 + 4 + server_data.length + 4;
var data = new WritableTrackingBuffer(bufferLength);
data.position = 0;
data.writeString('NTLMSSP\u0000', 'utf8');
data.writeUInt32LE(0x03);
var baseIdx = 64;
var dnIdx = baseIdx;
var unIdx = dnIdx + domain.length * 2;
var l2Idx = unIdx + username.length * 2;
var ntIdx = l2Idx + lmv2len;
data.writeUInt16LE(lmv2len);
data.writeUInt16LE(lmv2len);
data.writeUInt32LE(l2Idx);
data.writeUInt16LE(ntlmv2len);
data.writeUInt16LE(ntlmv2len);
data.writeUInt32LE(ntIdx);
data.writeUInt16LE(domain.length * 2);
data.writeUInt16LE(domain.length * 2);
data.writeUInt32LE(dnIdx);
data.writeUInt16LE(username.length * 2);
data.writeUInt16LE(username.length * 2);
data.writeUInt32LE(unIdx);
data.writeUInt16LE(0);
data.writeUInt16LE(0);
data.writeUInt32LE(baseIdx);
data.writeUInt16LE(0);
data.writeUInt16LE(0);
data.writeUInt32LE(baseIdx);
data.writeUInt16LE(0x8201);
data.writeUInt16LE(0x08);
data.writeString(domain, 'ucs2');
data.writeString(username, 'ucs2');
var lmv2Data = this.lmv2Response(domain, username, password, server_nonce, client_nonce);
data.copyFrom(lmv2Data);
var genTime = new Date().getTime();
ntlmData = this.ntlmv2Response(domain, username, password, server_nonce, server_data, client_nonce, genTime);
data.copyFrom(ntlmData);
data.writeUInt32LE(0x0101);
data.writeUInt32LE(0x0000);
var timestamp = this.createTimestamp(genTime);
data.copyFrom(timestamp);
data.copyFrom(client_nonce);
data.writeUInt32LE(0x0000);
data.copyFrom(server_data);
data.writeUInt32LE(0x0000);
return data.data;
}
}, {
key: 'createClientNonce',
value: function createClientNonce() {
var client_nonce = new Buffer(8);
var nidx = 0;
while (nidx < 8) {
client_nonce.writeUInt8(Math.ceil(Math.random() * 255), nidx);
nidx++;
}
return client_nonce;
}
}, {
key: 'ntlmv2Response',
value: function ntlmv2Response(domain, user, password, serverNonce, targetInfo, clientNonce, mytime) {
var timestamp = this.createTimestamp(mytime);
var hash = this.ntv2Hash(domain, user, password);
var dataLength = 40 + targetInfo.length;
var data = new Buffer(dataLength);
serverNonce.copy(data, 0, 0, 8);
data.writeUInt32LE(0x101, 8);
data.writeUInt32LE(0x0, 12);
timestamp.copy(data, 16, 0, 8);
clientNonce.copy(data, 24, 0, 8);
data.writeUInt32LE(0x0, 32);
targetInfo.copy(data, 36, 0, targetInfo.length);
data.writeUInt32LE(0x0, 36 + targetInfo.length);
return this.hmacMD5(data, hash);
}
}, {
key: 'createTimestamp',
value: function createTimestamp(time) {
var tenthsOfAMicrosecond = new BigInteger(time).plus(11644473600).multiply(10000000);
var hexArray = [];
var pair = [];
while (tenthsOfAMicrosecond.val() !== '0') {
var idx = tenthsOfAMicrosecond.mod(16);
pair.unshift(hex[idx]);
if (pair.length === 2) {
hexArray.push(pair.join(''));
pair = [];
}
}
if (pair.length > 0) {
hexArray.push(pair[0] + '0');
}
return new Buffer(hexArray.join(''), 'hex');
}
}, {
key: 'lmv2Response',
value: function lmv2Response(domain, user, password, serverNonce, clientNonce) {
var hash = this.ntv2Hash(domain, user, password);
var data = new Buffer(serverNonce.length + clientNonce.length);
serverNonce.copy(data);
clientNonce.copy(data, serverNonce.length, 0, clientNonce.length);
var newhash = this.hmacMD5(data, hash);
var response = new Buffer(newhash.length + clientNonce.length);
newhash.copy(response);
clientNonce.copy(response, newhash.length, 0, clientNonce.length);
return response;
}
}, {
key: 'ntv2Hash',
value: function ntv2Hash(domain, user, password) {
var hash = this.ntHash(password);
var identity = new Buffer(user.toUpperCase() + domain.toUpperCase(), 'ucs2');
return this.hmacMD5(identity, hash);
}
}, {
key: 'ntHash',
value: function ntHash(text) {
var hash = new Buffer(21);
hash.fill(0);
var unicodeString = new Buffer(text, 'ucs2');
var md4 = crypto.createHash('md4').update(unicodeString).digest();
if (md4.copy) {
md4.copy(hash);
} else {
new Buffer(md4, 'ascii').copy(hash);
}
return hash;
}
}, {
key: 'hmacMD5',
value: function hmacMD5(data, key) {
var hmac = crypto.createHmac('MD5', key);
hmac.update(data);
var result = hmac.digest();
if (result.copy) {
return result;
} else {
return new Buffer(result, 'ascii').slice(0, 16);
}
}
}]);
return NTLMResponsePayload;
})();
@@ -1,245 +0,0 @@
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
require('./buffertools');
var sprintf = require('sprintf').sprintf;
var HEADER_LENGTH = module.exports.HEADER_LENGTH = 8;
var TYPE = module.exports.TYPE = {
SQL_BATCH: 0x01,
RPC_REQUEST: 0x03,
TABULAR_RESULT: 0x04,
ATTENTION: 0x06,
BULK_LOAD: 0x07,
TRANSACTION_MANAGER: 0x0E,
LOGIN7: 0x10,
NTLMAUTH_PKT: 0x11,
PRELOGIN: 0x12
};
var typeByValue = {};
for (var _name in TYPE) {
typeByValue[TYPE[_name]] = _name;
}
var STATUS = {
NORMAL: 0x00,
EOM: 0x01,
IGNORE: 0x02,
RESETCONNECTION: 0x08,
RESETCONNECTIONSKIPTRAN: 0x10
};
var OFFSET = module.exports.OFFSET = {
Type: 0,
Status: 1,
Length: 2,
SPID: 4,
PacketID: 6,
Window: 7
};
var DEFAULT_SPID = 0;
var DEFAULT_PACKETID = 1;
var DEFAULT_WINDOW = 0;
var NL = '\n';
var Packet = (function () {
function Packet(typeOrBuffer) {
_classCallCheck(this, Packet);
if (typeOrBuffer instanceof Buffer) {
this.buffer = typeOrBuffer;
} else {
var type = typeOrBuffer;
this.buffer = new Buffer(HEADER_LENGTH);
this.buffer.writeUInt8(type, OFFSET.Type);
this.buffer.writeUInt8(STATUS.NORMAL, OFFSET.Status);
this.buffer.writeUInt16BE(DEFAULT_SPID, OFFSET.SPID);
this.buffer.writeUInt8(DEFAULT_PACKETID, OFFSET.PacketID);
this.buffer.writeUInt8(DEFAULT_WINDOW, OFFSET.Window);
this.setLength();
}
}
_createClass(Packet, [{
key: 'setLength',
value: function setLength() {
return this.buffer.writeUInt16BE(this.buffer.length, OFFSET.Length);
}
}, {
key: 'length',
value: function length() {
return this.buffer.readUInt16BE(OFFSET.Length);
}
}, {
key: 'resetConnection',
value: function resetConnection(reset) {
var status = this.buffer.readUInt8(OFFSET.Status);
if (reset) {
status |= STATUS.RESETCONNECTION;
} else {
status &= 0xFF - STATUS.RESETCONNECTION;
}
return this.buffer.writeUInt8(status, OFFSET.Status);
}
}, {
key: 'last',
value: function last(_last) {
var status = this.buffer.readUInt8(OFFSET.Status);
if (arguments.length > 0) {
if (_last) {
status |= STATUS.EOM;
} else {
status &= 0xFF - STATUS.EOM;
}
this.buffer.writeUInt8(status, OFFSET.Status);
}
return this.isLast();
}
}, {
key: 'isLast',
value: function isLast() {
return !!(this.buffer.readUInt8(OFFSET.Status) & STATUS.EOM);
}
}, {
key: 'packetId',
value: function packetId(_packetId) {
if (_packetId) {
this.buffer.writeUInt8(_packetId % 256, OFFSET.PacketID);
}
return this.buffer.readUInt8(OFFSET.PacketID);
}
}, {
key: 'addData',
value: function addData(data) {
this.buffer = Buffer.concat([this.buffer, data]);
this.setLength();
return this;
}
}, {
key: 'data',
value: function data() {
return this.buffer.slice(HEADER_LENGTH);
}
}, {
key: 'type',
value: function type() {
return this.buffer.readUInt8(OFFSET.Type);
}
}, {
key: 'statusAsString',
value: function statusAsString() {
var status = this.buffer.readUInt8(OFFSET.Status);
var statuses = [];
for (var _name2 in STATUS) {
var value = STATUS[_name2];
if (status & value) {
statuses.push(_name2);
} else {
statuses.push(undefined);
}
}
return statuses.join(' ').trim();
}
}, {
key: 'headerToString',
value: function headerToString(indent) {
indent || (indent = '');
var text = sprintf('type:0x%02X(%s), status:0x%02X(%s), length:0x%04X, spid:0x%04X, packetId:0x%02X, window:0x%02X', this.buffer.readUInt8(OFFSET.Type), typeByValue[this.buffer.readUInt8(OFFSET.Type)], this.buffer.readUInt8(OFFSET.Status), this.statusAsString(), this.buffer.readUInt16BE(OFFSET.Length), this.buffer.readUInt16BE(OFFSET.SPID), this.buffer.readUInt8(OFFSET.PacketID), this.buffer.readUInt8(OFFSET.Window));
return indent + text;
}
}, {
key: 'dataToString',
value: function dataToString(indent) {
indent || (indent = '');
var BYTES_PER_GROUP = 0x04;
var CHARS_PER_GROUP = 0x08;
var BYTES_PER_LINE = 0x20;
var data = this.data();
var dataDump = '';
var chars = '';
for (var offset = 0; offset < data.length; offset++) {
if (offset % BYTES_PER_LINE === 0) {
dataDump += indent;
dataDump += sprintf('%04X ', offset);
}
if (data[offset] < 0x20 || data[offset] > 0x7E) {
chars += '.';
if ((offset + 1) % CHARS_PER_GROUP === 0 && !((offset + 1) % BYTES_PER_LINE === 0)) {
chars += ' ';
}
} else {
chars += String.fromCharCode(data[offset]);
}
if (data[offset] != null) {
dataDump += sprintf('%02X', data[offset]);
}
if ((offset + 1) % BYTES_PER_GROUP === 0 && !((offset + 1) % BYTES_PER_LINE === 0)) {
dataDump += ' ';
}
if ((offset + 1) % BYTES_PER_LINE === 0) {
dataDump += ' ' + chars;
chars = '';
if (offset < data.length - 1) {
dataDump += NL;
}
}
}
if (chars.length) {
dataDump += ' ' + chars;
}
return dataDump;
}
}, {
key: 'toString',
value: function toString(indent) {
indent || (indent = '');
return this.headerToString(indent) + '\n' + this.dataToString(indent + indent);
}
}, {
key: 'payloadString',
value: function payloadString() {
return '';
}
}]);
return Packet;
})();
module.exports.Packet = Packet;
module.exports.isPacketComplete = isPacketComplete;
function isPacketComplete(potentialPacketBuffer) {
if (potentialPacketBuffer.length < HEADER_LENGTH) {
return false;
} else {
return potentialPacketBuffer.length >= potentialPacketBuffer.readUInt16BE(OFFSET.Length);
}
}
module.exports.packetLength = packetLength;
function packetLength(potentialPacketBuffer) {
return potentialPacketBuffer.readUInt16BE(OFFSET.Length);
}
@@ -1,220 +0,0 @@
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var sprintf = require('sprintf').sprintf;
var WritableTrackingBuffer = require('./tracking-buffer/tracking-buffer').WritableTrackingBuffer;
var optionBufferSize = 20;
var VERSION = 0x000000001;
var SUBBUILD = 0x0001;
var TOKEN = {
VERSION: 0x00,
ENCRYPTION: 0x01,
INSTOPT: 0x02,
THREADID: 0x03,
MARS: 0x04,
TERMINATOR: 0xFF
};
var ENCRYPT = {
OFF: 0x00,
ON: 0x01,
NOT_SUP: 0x02,
REQ: 0x03
};
var encryptByValue = {};
for (var _name in ENCRYPT) {
var value = ENCRYPT[_name];
encryptByValue[value] = _name;
}
var MARS = {
OFF: 0x00,
ON: 0x01
};
var marsByValue = {};
for (var _name2 in MARS) {
var value = MARS[_name2];
marsByValue[value] = _name2;
}
/*
s2.2.6.4
*/
module.exports = (function () {
function PreloginPayload(bufferOrOptions) {
_classCallCheck(this, PreloginPayload);
if (bufferOrOptions instanceof Buffer) {
this.data = bufferOrOptions;
} else {
this.options = bufferOrOptions || {};
this.createOptions();
}
this.extractOptions();
}
_createClass(PreloginPayload, [{
key: 'createOptions',
value: function createOptions() {
var options = [this.createVersionOption(), this.createEncryptionOption(), this.createInstanceOption(), this.createThreadIdOption(), this.createMarsOption()];
var length = 0;
for (var i = 0, len = options.length; i < len; i++) {
var option = options[i];
length += 5 + option.data.length;
}
length++; // terminator
this.data = new Buffer(length);
var optionOffset = 0;
var optionDataOffset = 5 * options.length + 1;
for (var j = 0, len = options.length; j < len; j++) {
var option = options[j];
this.data.writeUInt8(option.token, optionOffset + 0);
this.data.writeUInt16BE(optionDataOffset, optionOffset + 1);
this.data.writeUInt16BE(option.data.length, optionOffset + 3);
optionOffset += 5;
option.data.copy(this.data, optionDataOffset);
optionDataOffset += option.data.length;
}
return this.data.writeUInt8(TOKEN.TERMINATOR, optionOffset);
}
}, {
key: 'createVersionOption',
value: function createVersionOption() {
var buffer = new WritableTrackingBuffer(optionBufferSize);
buffer.writeUInt32BE(VERSION);
buffer.writeUInt16BE(SUBBUILD);
return {
token: TOKEN.VERSION,
data: buffer.data
};
}
}, {
key: 'createEncryptionOption',
value: function createEncryptionOption() {
var buffer = new WritableTrackingBuffer(optionBufferSize);
if (this.options.encrypt) {
buffer.writeUInt8(ENCRYPT.ON);
} else {
buffer.writeUInt8(ENCRYPT.NOT_SUP);
}
return {
token: TOKEN.ENCRYPTION,
data: buffer.data
};
}
}, {
key: 'createInstanceOption',
value: function createInstanceOption() {
var buffer = new WritableTrackingBuffer(optionBufferSize);
buffer.writeUInt8(0x00);
return {
token: TOKEN.INSTOPT,
data: buffer.data
};
}
}, {
key: 'createThreadIdOption',
value: function createThreadIdOption() {
var buffer = new WritableTrackingBuffer(optionBufferSize);
buffer.writeUInt32BE(0x00);
return {
token: TOKEN.THREADID,
data: buffer.data
};
}
}, {
key: 'createMarsOption',
value: function createMarsOption() {
var buffer = new WritableTrackingBuffer(optionBufferSize);
buffer.writeUInt8(MARS.OFF);
return {
token: TOKEN.MARS,
data: buffer.data
};
}
}, {
key: 'extractOptions',
value: function extractOptions() {
var offset = 0;
while (this.data[offset] !== TOKEN.TERMINATOR) {
var dataOffset = this.data.readUInt16BE(offset + 1);
var dataLength = this.data.readUInt16BE(offset + 3);
switch (this.data[offset]) {
case TOKEN.VERSION:
this.extractVersion(dataOffset);
break;
case TOKEN.ENCRYPTION:
this.extractEncryption(dataOffset);
break;
case TOKEN.INSTOPT:
this.extractInstance(dataOffset);
break;
case TOKEN.THREADID:
if (dataLength > 0) {
this.extractThreadId(dataOffset);
}
break;
case TOKEN.MARS:
this.extractMars(dataOffset);
}
offset += 5;
dataOffset += dataLength;
}
}
}, {
key: 'extractVersion',
value: function extractVersion(offset) {
return this.version = {
major: this.data.readUInt8(offset + 0),
minor: this.data.readUInt8(offset + 1),
patch: this.data.readUInt8(offset + 2),
trivial: this.data.readUInt8(offset + 3),
subbuild: this.data.readUInt16BE(offset + 4)
};
}
}, {
key: 'extractEncryption',
value: function extractEncryption(offset) {
this.encryption = this.data.readUInt8(offset);
return this.encryptionString = encryptByValue[this.encryption];
}
}, {
key: 'extractInstance',
value: function extractInstance(offset) {
return this.instance = this.data.readUInt8(offset);
}
}, {
key: 'extractThreadId',
value: function extractThreadId(offset) {
return this.threadId = this.data.readUInt32BE(offset);
}
}, {
key: 'extractMars',
value: function extractMars(offset) {
this.mars = this.data.readUInt8(offset);
return this.marsString = marsByValue[this.mars];
}
}, {
key: 'toString',
value: function toString(indent) {
indent || (indent = '');
return indent + 'PreLogin - ' + sprintf('version:%d.%d.%d.%d %d, encryption:0x%02X(%s), instopt:0x%02X, threadId:0x%08X, mars:0x%02X(%s)', this.version.major, this.version.minor, this.version.patch, this.version.trivial, this.version.subbuild, this.encryption ? this.encryption : 0, this.encryptionString ? this.encryptionString : 0, this.instance ? this.instance : 0, this.threadId ? this.threadId : 0, this.mars ? this.mars : 0, this.marsString ? this.marsString : 0);
}
}]);
return PreloginPayload;
})();
@@ -1,165 +0,0 @@
'use strict';
var _get = require('babel-runtime/helpers/get')['default'];
var _inherits = require('babel-runtime/helpers/inherits')['default'];
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var EventEmitter = require('events').EventEmitter;
var TYPES = require('./data-type').typeByName;
var RequestError = require('./errors').RequestError;
module.exports = (function (_EventEmitter) {
_inherits(Request, _EventEmitter);
function Request(sqlTextOrProcedure, callback) {
_classCallCheck(this, Request);
_get(Object.getPrototypeOf(Request.prototype), 'constructor', this).call(this);
this.sqlTextOrProcedure = sqlTextOrProcedure;
this.callback = callback;
this.parameters = [];
this.parametersByName = {};
this.userCallback = this.callback;
this.callback = function () {
if (this.preparing) {
this.emit('prepared');
return this.preparing = false;
} else {
this.userCallback.apply(this, arguments);
return this.emit('requestCompleted');
}
};
}
_createClass(Request, [{
key: 'addParameter',
value: function addParameter(name, type, value, options) {
if (options == null) {
options = {};
}
var parameter = {
type: type,
name: name,
value: value,
output: options.output || (options.output = false),
length: options.length,
precision: options.precision,
scale: options.scale
};
this.parameters.push(parameter);
return this.parametersByName[name] = parameter;
}
}, {
key: 'addOutputParameter',
value: function addOutputParameter(name, type, value, options) {
if (options == null) {
options = {};
}
options.output = true;
return this.addParameter(name, type, value, options);
}
}, {
key: 'makeParamsParameter',
value: function makeParamsParameter(parameters) {
var paramsParameter = '';
for (var i = 0, len = parameters.length; i < len; i++) {
var parameter = parameters[i];
if (paramsParameter.length > 0) {
paramsParameter += ', ';
}
paramsParameter += '@' + parameter.name + ' ';
paramsParameter += parameter.type.declaration(parameter);
if (parameter.output) {
paramsParameter += ' OUTPUT';
}
}
return paramsParameter;
}
}, {
key: 'transformIntoExecuteSqlRpc',
value: function transformIntoExecuteSqlRpc() {
if (this.validateParameters()) {
return;
}
this.originalParameters = this.parameters;
this.parameters = [];
this.addParameter('statement', TYPES.NVarChar, this.sqlTextOrProcedure);
if (this.originalParameters.length) {
this.addParameter('params', TYPES.NVarChar, this.makeParamsParameter(this.originalParameters));
}
for (var i = 0, len = this.originalParameters.length; i < len; i++) {
var parameter = this.originalParameters[i];
this.parameters.push(parameter);
}
return this.sqlTextOrProcedure = 'sp_executesql';
}
}, {
key: 'transformIntoPrepareRpc',
value: function transformIntoPrepareRpc() {
var _this = this;
this.originalParameters = this.parameters;
this.parameters = [];
this.addOutputParameter('handle', TYPES.Int);
this.addParameter('params', TYPES.NVarChar, this.makeParamsParameter(this.originalParameters));
this.addParameter('stmt', TYPES.NVarChar, this.sqlTextOrProcedure);
this.sqlTextOrProcedure = 'sp_prepare';
this.preparing = true;
return this.on('returnValue', function (name, value) {
if (name === 'handle') {
return _this.handle = value;
} else {
return _this.error = RequestError('Tedious > Unexpected output parameter ' + name + ' from sp_prepare');
}
});
}
}, {
key: 'transformIntoUnprepareRpc',
value: function transformIntoUnprepareRpc() {
this.parameters = [];
this.addParameter('handle', TYPES.Int, this.handle);
return this.sqlTextOrProcedure = 'sp_unprepare';
}
}, {
key: 'transformIntoExecuteRpc',
value: function transformIntoExecuteRpc(parameters) {
this.parameters = [];
this.addParameter('handle', TYPES.Int, this.handle);
for (var i = 0, len = this.originalParameters.length; i < len; i++) {
var parameter = this.originalParameters[i];
parameter.value = parameters[parameter.name];
this.parameters.push(parameter);
}
if (this.validateParameters()) {
return;
}
return this.sqlTextOrProcedure = 'sp_execute';
}
}, {
key: 'validateParameters',
value: function validateParameters() {
for (var i = 0, len = this.parameters.length; i < len; i++) {
var parameter = this.parameters[i];
var value = parameter.type.validate(parameter.value);
if (value instanceof TypeError) {
return this.error = new RequestError('Validation failed for parameter \'' + parameter.name + '\'. ' + value.message, 'EPARAM');
}
parameter.value = value;
}
return null;
}
}]);
return Request;
})(EventEmitter);
@@ -1,104 +0,0 @@
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var WritableTrackingBuffer = require('./tracking-buffer/tracking-buffer').WritableTrackingBuffer;
var writeAllHeaders = require('./all-headers').writeToTrackingBuffer;
// const OPTION = {
// WITH_RECOMPILE: 0x01,
// NO_METADATA: 0x02,
// REUSE_METADATA: 0x04
// };
var STATUS = {
BY_REF_VALUE: 0x01,
DEFAULT_VALUE: 0x02
};
/*
s2.2.6.5
*/
module.exports = (function () {
function RpcRequestPayload(request, txnDescriptor, options) {
_classCallCheck(this, RpcRequestPayload);
this.request = request;
this.procedure = this.request.sqlTextOrProcedure;
var buffer = new WritableTrackingBuffer(500);
if (options.tdsVersion >= '7_2') {
var outstandingRequestCount = 1;
writeAllHeaders(buffer, txnDescriptor, outstandingRequestCount);
}
if (typeof this.procedure === 'string') {
buffer.writeUsVarchar(this.procedure);
} else {
buffer.writeUShort(0xFFFF);
buffer.writeUShort(this.procedure);
}
var optionFlags = 0;
buffer.writeUInt16LE(optionFlags);
var parameters = this.request.parameters;
for (var i = 0, len = parameters.length; i < len; i++) {
var parameter = parameters[i];
buffer.writeBVarchar('@' + parameter.name);
var statusFlags = 0;
if (parameter.output) {
statusFlags |= STATUS.BY_REF_VALUE;
}
buffer.writeUInt8(statusFlags);
var param = {
value: parameter.value
};
var type = parameter.type;
if ((type.id & 0x30) === 0x20) {
if (parameter.length) {
param.length = parameter.length;
} else if (type.resolveLength) {
param.length = type.resolveLength(parameter);
}
}
if (type.hasPrecision) {
if (parameter.precision) {
param.precision = parameter.precision;
} else if (type.resolvePrecision) {
param.precision = type.resolvePrecision(parameter);
}
}
if (type.hasScale) {
if (parameter.scale) {
param.scale = parameter.scale;
} else if (type.resolveScale) {
param.scale = type.resolveScale(parameter);
}
}
type.writeTypeInfo(buffer, param, options);
type.writeParameterData(buffer, param, options);
}
this.data = buffer.data;
}
_createClass(RpcRequestPayload, [{
key: 'toString',
value: function toString(indent) {
indent || (indent = '');
return indent + ('RPC Request - ' + this.procedure);
}
}]);
return RpcRequestPayload;
})();
@@ -1,19 +0,0 @@
'use strict';
module.exports = {
Sp_Cursor: 1,
Sp_CursorOpen: 2,
Sp_CursorPrepare: 3,
Sp_CursorExecute: 4,
Sp_CursorPrepExec: 5,
Sp_CursorUnprepare: 6,
Sp_CursorFetch: 7,
Sp_CursorOption: 8,
Sp_CursorClose: 9,
Sp_ExecuteSql: 10,
Sp_Prepare: 11,
Sp_Execute: 12,
Sp_PrepExec: 13,
Sp_PrepExecRpc: 14,
Sp_Unprepare: 15
};
@@ -1,37 +0,0 @@
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var WritableTrackingBuffer = require('./tracking-buffer/tracking-buffer').WritableTrackingBuffer;
var writeAllHeaders = require('./all-headers').writeToTrackingBuffer;
/*
s2.2.6.6
*/
module.exports = (function () {
function SqlBatchPayload(sqlText, txnDescriptor, options) {
_classCallCheck(this, SqlBatchPayload);
this.sqlText = sqlText;
var buffer = new WritableTrackingBuffer(100 + 2 * this.sqlText.length, 'ucs2');
if (options.tdsVersion >= '7_2') {
var outstandingRequestCount = 1;
writeAllHeaders(buffer, txnDescriptor, outstandingRequestCount);
}
buffer.writeString(this.sqlText, 'ucs2');
this.data = buffer.data;
}
_createClass(SqlBatchPayload, [{
key: 'toString',
value: function toString(indent) {
indent || (indent = '');
return indent + ('SQL Batch - ' + this.sqlText);
}
}]);
return SqlBatchPayload;
})();
@@ -1,174 +0,0 @@
'use strict';
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var _get = require('babel-runtime/helpers/get')['default'];
var _inherits = require('babel-runtime/helpers/inherits')['default'];
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _Object$keys = require('babel-runtime/core-js/object/keys')['default'];
var stream = require('readable-stream');
var BufferList = require('bl');
var Job = function Job(length, execute) {
_classCallCheck(this, Job);
this.length = length;
this.execute = execute;
}
// These jobs are non-dynamic, so we can reuse the job objects.
// This should reduce GC pressure a bit (as less objects will be
// created and garbage collected during stream parsing).
;
var JOBS = {
readInt8: new Job(1, function (buffer, offset) {
return buffer.readInt8(offset);
}),
readUInt8: new Job(1, function (buffer, offset) {
return buffer.readUInt8(offset);
}),
readInt16LE: new Job(2, function (buffer, offset) {
return buffer.readInt16LE(offset);
}),
readInt16BE: new Job(2, function (buffer, offset) {
return buffer.readInt16BE(offset);
}),
readUInt16LE: new Job(2, function (buffer, offset) {
return buffer.readUInt16LE(offset);
}),
readUInt16BE: new Job(2, function (buffer, offset) {
return buffer.readUInt16BE(offset);
}),
readInt32LE: new Job(4, function (buffer, offset) {
return buffer.readInt32LE(offset);
}),
readInt32BE: new Job(4, function (buffer, offset) {
return buffer.readInt32BE(offset);
}),
readUInt32LE: new Job(4, function (buffer, offset) {
return buffer.readUInt32LE(offset);
}),
readUInt32BE: new Job(4, function (buffer, offset) {
return buffer.readUInt32BE(offset);
}),
readInt64LE: new Job(8, function (buffer, offset) {
return Math.pow(2, 32) * buffer.readInt32LE(offset + 4) + (buffer[offset + 4] & 0x80 === 0x80 ? 1 : -1) * buffer.readUInt32LE(offset);
}),
readInt64BE: new Job(8, function (buffer, offset) {
return Math.pow(2, 32) * buffer.readInt32BE(offset) + (buffer[offset] & 0x80 === 0x80 ? 1 : -1) * buffer.readUInt32BE(offset + 4);
}),
readUInt64LE: new Job(8, function (buffer, offset) {
return Math.pow(2, 32) * buffer.readUInt32LE(offset + 4) + buffer.readUInt32LE(offset);
}),
readUInt64BE: new Job(8, function (buffer, offset) {
return Math.pow(2, 32) * buffer.readUInt32BE(offset) + buffer.readUInt32BE(offset + 4);
}),
readFloatLE: new Job(4, function (buffer, offset) {
return buffer.readFloatLE(offset);
}),
readFloatBE: new Job(4, function (buffer, offset) {
return buffer.readFloatBE(offset);
}),
readDoubleLE: new Job(8, function (buffer, offset) {
return buffer.readDoubleLE(offset);
}),
readDoubleBE: new Job(8, function (buffer, offset) {
return buffer.readDoubleBE(offset);
})
};
var StreamParser = (function (_stream$Transform) {
_inherits(StreamParser, _stream$Transform);
function StreamParser(options) {
_classCallCheck(this, StreamParser);
options = options || {};
if (options.objectMode === undefined) {
options.objectMode = true;
}
_get(Object.getPrototypeOf(StreamParser.prototype), 'constructor', this).call(this, options);
this.buffer = new BufferList();
this.generator = undefined;
this.currentStep = undefined;
}
_createClass(StreamParser, [{
key: 'parser',
value: function parser() {
throw new Error('Not implemented');
}
}, {
key: '_transform',
value: function _transform(input, encoding, done) {
this.buffer.append(input);
if (!this.generator) {
this.generator = this.parser();
this.currentStep = this.generator.next();
}
var offset = 0;
while (!this.currentStep.done) {
var job = this.currentStep.value;
if (!(job instanceof Job)) {
return done(new Error('invalid job type'));
}
var _length = job.length;
if (this.buffer.length - offset < _length) {
break;
}
var result = job.execute(this.buffer, offset);
offset += _length;
this.currentStep = this.generator.next(result);
}
this.buffer.consume(offset);
if (this.currentStep.done) {
this.push(null);
}
done();
}
}, {
key: 'readBuffer',
value: function readBuffer(length) {
return new Job(length, function (buffer, offset) {
return buffer.slice(offset, offset + length);
});
}
}, {
key: 'readString',
value: function readString(length) {
return new Job(length, function (buffer, offset) {
return buffer.toString('utf8', offset, offset + length);
});
}
}, {
key: 'skip',
value: function skip(length) {
return new Job(length, function () {});
}
}]);
return StreamParser;
})(stream.Transform);
module.exports = StreamParser;
_Object$keys(JOBS).forEach(function (jobName) {
return StreamParser.prototype[jobName] = function () {
return JOBS[jobName];
};
});
@@ -1,15 +0,0 @@
'use strict';
var versions = module.exports.versions = {
'7_1': 0x71000001,
'7_2': 0x72090002,
'7_3_A': 0x730A0003,
'7_3_B': 0x730B0003,
'7_4': 0x74000004
};
var versionsByValue = module.exports.versionsByValue = {};
for (var _name in versions) {
versionsByValue[versions[_name]] = _name;
}
@@ -1,10 +0,0 @@
'use strict';
module.exports.BulkLoad = require('./bulk-load');
module.exports.Connection = require('./connection');
module.exports.Request = require('./request');
module.exports.library = require('./library');
module.exports.TYPES = require('./data-type').typeByName;
module.exports.ISOLATION_LEVEL = require('./transaction').ISOLATION_LEVEL;
module.exports.TDS_VERSION = require('./tds-versions').versions;
@@ -1,99 +0,0 @@
'use strict';
var metadataParse = require('../metadata-parser');
function readTableName(parser, options, metadata, callback) {
if (metadata.type.hasTableName) {
if (options.tdsVersion >= '7_2') {
parser.readUInt8(function (numberOfTableNameParts) {
var tableName = [];
var i = 0;
function next(done) {
if (numberOfTableNameParts === i) {
return done();
}
parser.readUsVarChar(function (part) {
tableName.push(part);
i++;
next(done);
});
}
next(function () {
callback(tableName);
});
});
} else {
parser.readUsVarChar(callback);
}
} else {
callback(undefined);
}
}
function readColumnName(parser, options, index, metadata, callback) {
parser.readBVarChar(function (colName) {
if (options.columnNameReplacer) {
callback(options.columnNameReplacer(colName, index, metadata));
} else if (options.camelCaseColumns) {
callback(colName.replace(/^[A-Z]/, function (s) {
return s.toLowerCase();
}));
} else {
callback(colName);
}
});
}
function readColumn(parser, options, index, callback) {
metadataParse(parser, options, function (metadata) {
readTableName(parser, options, metadata, function (tableName) {
readColumnName(parser, options, index, metadata, function (colName) {
callback({
userType: metadata.userType,
flags: metadata.flags,
type: metadata.type,
colName: colName,
collation: metadata.collation,
precision: metadata.precision,
scale: metadata.scale,
udtInfo: metadata.udtInfo,
dataLength: metadata.dataLength,
tableName: tableName
});
});
});
});
}
module.exports = function (parser, colMetadata, options, callback) {
parser.readUInt16LE(function (columnCount) {
var columns = [];
var i = 0;
function next(done) {
if (i === columnCount) {
return done();
}
readColumn(parser, options, i, function (column) {
columns.push(column);
i++;
next(done);
});
}
next(function () {
callback({
name: 'COLMETADATA',
event: 'columnMetadata',
columns: columns
});
});
});
};

Some files were not shown because too many files have changed in this diff Show More