diff --git a/sql-odbc/.gitignore b/sql-odbc/.gitignore index 849c3e99b7..ecaefb1c61 100644 --- a/sql-odbc/.gitignore +++ b/sql-odbc/.gitignore @@ -57,4 +57,5 @@ CTestTestfile.cmake /sdk-build64/ /cmake-build64/ /src/PowerBIConnector/bin/Debug/ -/src/PowerBIConnector/obj/ \ No newline at end of file +/src/PowerBIConnector/obj/ +/src/PowerBIConnector/.vs/ diff --git a/sql-odbc/docs/user/img/pbi_auth.png b/sql-odbc/docs/user/img/pbi_auth.png index 12e1af8ed6..0795d63d95 100644 Binary files a/sql-odbc/docs/user/img/pbi_auth.png and b/sql-odbc/docs/user/img/pbi_auth.png differ diff --git a/sql-odbc/docs/user/img/pbi_error_conn.png b/sql-odbc/docs/user/img/pbi_error_conn.png new file mode 100644 index 0000000000..87f908a397 Binary files /dev/null and b/sql-odbc/docs/user/img/pbi_error_conn.png differ diff --git a/sql-odbc/docs/user/img/pbi_error_driver_not_installed.png b/sql-odbc/docs/user/img/pbi_error_driver_not_installed.png new file mode 100644 index 0000000000..b5059bc0b4 Binary files /dev/null and b/sql-odbc/docs/user/img/pbi_error_driver_not_installed.png differ diff --git a/sql-odbc/docs/user/power_bi_support.md b/sql-odbc/docs/user/power_bi_support.md index 38b8259a42..46dd618829 100644 --- a/sql-odbc/docs/user/power_bi_support.md +++ b/sql-odbc/docs/user/power_bi_support.md @@ -32,7 +32,7 @@ -* Select authentication option and click on **Connect**. +* Select authentication option. Enter credentials if required and click on **Connect**. @@ -40,4 +40,19 @@ -* Click on **Load**. \ No newline at end of file +* Click on **Load**. + +## Troubleshooting + +* If you get an following error, please install [Open Distro For Elasticsearch SQL ODBC Driver](https://opendistro.github.io/for-elasticsearch-docs/docs/sql/odbc/). + + + +* If you get an following error, + + + +1. Check if host and port values are correct. +2. Check if auth credentials are correct. +3. Check if server is running. + diff --git a/sql-odbc/src/PowerBIConnector/Diagnostics.pqm b/sql-odbc/src/PowerBIConnector/Diagnostics.pqm new file mode 100644 index 0000000000..ca10c3cf98 --- /dev/null +++ b/sql-odbc/src/PowerBIConnector/Diagnostics.pqm @@ -0,0 +1,275 @@ +let + Diagnostics.LogValue = (prefix, value) => Diagnostics.Trace(TraceLevel.Information, prefix & ": " & (try Diagnostics.ValueToText(value) otherwise ""), value), + Diagnostics.LogValue2 = (prefix, value, result, optional delayed) => Diagnostics.Trace(TraceLevel.Information, prefix & ": " & Diagnostics.ValueToText(value), result, delayed), + Diagnostics.LogFailure = (text, function) => + let + result = try function() + in + if result[HasError] then Diagnostics.LogValue2(text, result[Error], () => error result[Error], true) else result[Value], + + Diagnostics.WrapFunctionResult = (innerFunction as function, outerFunction as function) as function => + Function.From(Value.Type(innerFunction), (list) => outerFunction(() => Function.Invoke(innerFunction, list))), + + Diagnostics.WrapHandlers = (handlers as record) as record => + Record.FromList( + List.Transform( + Record.FieldNames(handlers), + (h) => Diagnostics.WrapFunctionResult(Record.Field(handlers, h), (fn) => Diagnostics.LogFailure(h, fn))), + Record.FieldNames(handlers)), + + Diagnostics.ValueToText = (value) => + let + _canBeIdentifier = (x) => + let + keywords = {"and", "as", "each", "else", "error", "false", "if", "in", "is", "let", "meta", "not", "otherwise", "or", "section", "shared", "then", "true", "try", "type" }, + charAlpha = (c as number) => (c>= 65 and c <= 90) or (c>= 97 and c <= 122) or c=95, + charDigit = (c as number) => c>= 48 and c <= 57 + in + try + charAlpha(Character.ToNumber(Text.At(x,0))) + and + List.MatchesAll( + Text.ToList(x), + (c)=> let num = Character.ToNumber(c) in charAlpha(num) or charDigit(num) + ) + and not + List.MatchesAny( keywords, (li)=> li=x ) + otherwise + false, + + Serialize.Binary = (x) => "#binary(" & Serialize(Binary.ToList(x)) & ") ", + + Serialize.Date = (x) => "#date(" & + Text.From(Date.Year(x)) & ", " & + Text.From(Date.Month(x)) & ", " & + Text.From(Date.Day(x)) & ") ", + + Serialize.Datetime = (x) => "#datetime(" & + Text.From(Date.Year(DateTime.Date(x))) & ", " & + Text.From(Date.Month(DateTime.Date(x))) & ", " & + Text.From(Date.Day(DateTime.Date(x))) & ", " & + Text.From(Time.Hour(DateTime.Time(x))) & ", " & + Text.From(Time.Minute(DateTime.Time(x))) & ", " & + Text.From(Time.Second(DateTime.Time(x))) & ") ", + + Serialize.Datetimezone =(x) => let + dtz = DateTimeZone.ToRecord(x) + in + "#datetimezone(" & + Text.From(dtz[Year]) & ", " & + Text.From(dtz[Month]) & ", " & + Text.From(dtz[Day]) & ", " & + Text.From(dtz[Hour]) & ", " & + Text.From(dtz[Minute]) & ", " & + Text.From(dtz[Second]) & ", " & + Text.From(dtz[ZoneHours]) & ", " & + Text.From(dtz[ZoneMinutes]) & ") ", + + Serialize.Duration = (x) => let + dur = Duration.ToRecord(x) + in + "#duration(" & + Text.From(dur[Days]) & ", " & + Text.From(dur[Hours]) & ", " & + Text.From(dur[Minutes]) & ", " & + Text.From(dur[Seconds]) & ") ", + + Serialize.Function = (x) => _serialize_function_param_type( + Type.FunctionParameters(Value.Type(x)), + Type.FunctionRequiredParameters(Value.Type(x)) ) & + " as " & + _serialize_function_return_type(Value.Type(x)) & + " => (...) ", + + Serialize.List = (x) => "{" & + List.Accumulate(x, "", (seed,item) => if seed="" then Serialize(item) else seed & ", " & Serialize(item)) & + "} ", + + Serialize.Logical = (x) => Text.From(x), + + Serialize.Null = (x) => "null", + + Serialize.Number = (x) => + let Text.From = (i as number) as text => + if Number.IsNaN(i) then "#nan" else + if i=Number.PositiveInfinity then "#infinity" else + if i=Number.NegativeInfinity then "-#infinity" else + Text.From(i) + in + Text.From(x), + + Serialize.Record = (x) => "[ " & + List.Accumulate( + Record.FieldNames(x), + "", + (seed,item) => + (if seed="" then Serialize.Identifier(item) else seed & ", " & Serialize.Identifier(item)) & " = " & Serialize(Record.Field(x, item)) + ) & + " ] ", + + Serialize.Table = (x) => "#table( type " & + _serialize_table_type(Value.Type(x)) & + ", " & + Serialize(Table.ToRows(x)) & + ") ", + + Serialize.Text = (x) => """" & + _serialize_text_content(x) & + """", + + _serialize_text_content = (x) => let + escapeText = (n as number) as text => "#(#)(" & Text.PadStart(Number.ToText(n, "X", "en-US"), 4, "0") & ")" + in + List.Accumulate( + List.Transform( + Text.ToList(x), + (c) => let n=Character.ToNumber(c) in + if n = 9 then "#(#)(tab)" else + if n = 10 then "#(#)(lf)" else + if n = 13 then "#(#)(cr)" else + if n = 34 then """""" else + if n = 35 then "#(#)(#)" else + if n < 32 then escapeText(n) else + if n < 127 then Character.FromNumber(n) else + escapeText(n) + ), + "", + (s,i)=>s&i + ), + + Serialize.Identifier = (x) => + if _canBeIdentifier(x) then + x + else + "#""" & + _serialize_text_content(x) & + """", + + Serialize.Time = (x) => "#time(" & + Text.From(Time.Hour(x)) & ", " & + Text.From(Time.Minute(x)) & ", " & + Text.From(Time.Second(x)) & ") ", + + Serialize.Type = (x) => "type " & _serialize_typename(x), + + + _serialize_typename = (x, optional funtype as logical) => /* Optional parameter: Is this being used as part of a function signature? */ + let + isFunctionType = (x as type) => try if Type.FunctionReturn(x) is type then true else false otherwise false, + isTableType = (x as type) => try if Type.TableSchema(x) is table then true else false otherwise false, + isRecordType = (x as type) => try if Type.ClosedRecord(x) is type then true else false otherwise false, + isListType = (x as type) => try if Type.ListItem(x) is type then true else false otherwise false + in + + if funtype=null and isTableType(x) then _serialize_table_type(x) else + if funtype=null and isListType(x) then "{ " & @_serialize_typename( Type.ListItem(x) ) & " }" else + if funtype=null and isFunctionType(x) then "function " & _serialize_function_type(x) else + if funtype=null and isRecordType(x) then _serialize_record_type(x) else + + if x = type any then "any" else + let base = Type.NonNullable(x) in + (if Type.IsNullable(x) then "nullable " else "") & + (if base = type anynonnull then "anynonnull" else + if base = type binary then "binary" else + if base = type date then "date" else + if base = type datetime then "datetime" else + if base = type datetimezone then "datetimezone" else + if base = type duration then "duration" else + if base = type logical then "logical" else + if base = type none then "none" else + if base = type null then "null" else + if base = type number then "number" else + if base = type text then "text" else + if base = type time then "time" else + if base = type type then "type" else + + /* Abstract types: */ + if base = type function then "function" else + if base = type table then "table" else + if base = type record then "record" else + if base = type list then "list" else + + "any /*Actually unknown type*/"), + + _serialize_table_type = (x) => + let + schema = Type.TableSchema(x) + in + "table " & + (if Table.IsEmpty(schema) then "" else + "[" & List.Accumulate( + List.Transform( + Table.ToRecords(Table.Sort(schema,"Position")), + each Serialize.Identifier(_[Name]) & " = " & _[Kind]), + "", + (seed,item) => (if seed="" then item else seed & ", " & item ) + ) & "] " ), + + _serialize_record_type = (x) => + let flds = Type.RecordFields(x) + in + if Record.FieldCount(flds)=0 then "record" else + "[" & List.Accumulate( + Record.FieldNames(flds), + "", + (seed,item) => + seed & + (if seed<>"" then ", " else "") & + (Serialize.Identifier(item) & "=" & _serialize_typename(Record.Field(flds,item)[Type]) ) + ) & + (if Type.IsOpenRecord(x) then ",..." else "") & + "]", + + _serialize_function_type = (x) => _serialize_function_param_type( + Type.FunctionParameters(x), + Type.FunctionRequiredParameters(x) ) & + " as " & + _serialize_function_return_type(x), + + _serialize_function_param_type = (t,n) => + let + funsig = Table.ToRecords( + Table.TransformColumns( + Table.AddIndexColumn( Record.ToTable( t ), "isOptional", 1 ), + { "isOptional", (x)=> x>n } ) ) + in + "(" & + List.Accumulate( + funsig, + "", + (seed,item)=> + (if seed="" then "" else seed & ", ") & + (if item[isOptional] then "optional " else "") & + Serialize.Identifier(item[Name]) & " as " & _serialize_typename(item[Value], true) ) + & ")", + + _serialize_function_return_type = (x) => _serialize_typename(Type.FunctionReturn(x), true), + + Serialize = (x) as text => + if x is binary then try Serialize.Binary(x) otherwise "null /*serialize failed*/" else + if x is date then try Serialize.Date(x) otherwise "null /*serialize failed*/" else + if x is datetime then try Serialize.Datetime(x) otherwise "null /*serialize failed*/" else + if x is datetimezone then try Serialize.Datetimezone(x) otherwise "null /*serialize failed*/" else + if x is duration then try Serialize.Duration(x) otherwise "null /*serialize failed*/" else + if x is function then try Serialize.Function(x) otherwise "null /*serialize failed*/" else + if x is list then try Serialize.List(x) otherwise "null /*serialize failed*/" else + if x is logical then try Serialize.Logical(x) otherwise "null /*serialize failed*/" else + if x is null then try Serialize.Null(x) otherwise "null /*serialize failed*/" else + if x is number then try Serialize.Number(x) otherwise "null /*serialize failed*/" else + if x is record then try Serialize.Record(x) otherwise "null /*serialize failed*/" else + if x is table then try Serialize.Table(x) otherwise "null /*serialize failed*/" else + if x is text then try Serialize.Text(x) otherwise "null /*serialize failed*/" else + if x is time then try Serialize.Time(x) otherwise "null /*serialize failed*/" else + if x is type then try Serialize.Type(x) otherwise "null /*serialize failed*/" else + "[#_unable_to_serialize_#]" + in + try Serialize(value) otherwise "" +in + [ + LogValue = Diagnostics.LogValue, + LogValue2 = Diagnostics.LogValue2, + LogFailure = Diagnostics.LogFailure, + WrapFunctionResult = Diagnostics.WrapFunctionResult, + WrapHandlers = Diagnostics.WrapHandlers, + ValueToText = Diagnostics.ValueToText + ] \ No newline at end of file diff --git a/sql-odbc/src/PowerBIConnector/OdbcConstants.pqm b/sql-odbc/src/PowerBIConnector/OdbcConstants.pqm new file mode 100644 index 0000000000..144e525413 --- /dev/null +++ b/sql-odbc/src/PowerBIConnector/OdbcConstants.pqm @@ -0,0 +1,1253 @@ +// values from https://github.com/Microsoft/ODBC-Specification/blob/master/Windows/inc/sqlext.h +[ + Flags = (flags as list) => + if (List.IsEmpty(flags)) then 0 else + let + Loop = List.Generate(()=> [i = 0, Combined = flags{0}], + each [i] < List.Count(flags), + each [Combined = Number.BitwiseOr([Combined], flags{i}), i = [i]+1], + each [Combined]), + Result = List.Last(Loop) + in + Result, + + SQL_HANDLE = + [ + ENV = 1, + DBC = 2, + STMT = 3, + DESC = 4 + ], + + RetCode = + [ + SUCCESS = 0, + SUCCESS_WITH_INFO = 1, + ERROR = -1, + INVALID_HANDLE = -2, + NO_DATA = 100 + ], + + SQL_CONVERT = + [ + BIGINT = 53, + BINARY = 54, + BIT = 55, + CHAR = 56, + DATE = 57, + DECIMAL = 58, + DOUBLE = 59, + FLOAT = 60, + INTEGER = 61, + LONGVARCHAR = 62, + NUMERIC = 63, + REAL = 64, + SMALLINT = 65, + TIME = 66, + TIMESTAMP = 67, + TINYINT = 68, + VARBINARY = 69, + VARCHAR = 70, + LONGVARBINARY = 71 + ], + + SQL_ROW = + [ + PROCEED = 0, + IGNORE = 1, + SUCCESS = 0, + DELETED = 1, + UPDATED = 2, + NOROW = 3, + ADDED = 4, + ERROR = 5, + SUCCESS_WITH_INFO = 6 + ], + +SQL_CVT = +[ + //None = 0, + + CHAR = 0x00000001, + NUMERIC = 0x00000002, + DECIMAL = 0x00000004, + INTEGER = 0x00000008, + SMALLINT = 0x00000010, + FLOAT = 0x00000020, + REAL = 0x00000040, + DOUBLE = 0x00000080, + VARCHAR = 0x00000100, + LONGVARCHAR = 0x00000200, + BINARY = 0x00000400, + VARBINARY = 0x00000800, + BIT = 0x00001000, + TINYINT = 0x00002000, + BIGINT = 0x00004000, + DATE = 0x00008000, + TIME = 0x00010000, + TIMESTAMP = 0x00020000, + LONGVARBINARY = 0x00040000, + INTERVAL_YEAR_MONTH = 0x00080000, + INTERVAL_DAY_TIME = 0x00100000, + WCHAR = 0x00200000, + WLONGVARCHAR = 0x00400000, + WVARCHAR = 0x00800000, + GUID = 0x01000000 +], + + STMT = + [ + CLOSE = 0, + DROP = 1, + UNBIND = 2, + RESET_PARAMS = 3 + ], + + SQL_MAX = + [ + NUMERIC_LEN = 16 + ], + + SQL_IS = + [ + POINTER = -4, + INTEGER = -6, + UINTEGER = -5, + SMALLINT = -8 + ], + + //SQL Server specific defines + // + SQL_HC = // from Odbcss.h + [ + OFF = 0, // FOR BROWSE columns are hidden + ON = 1 // FOR BROWSE columns are exposed + ], + + SQL_NB = // from Odbcss.h + [ + OFF = 0, // NO_BROWSETABLE is off + ON = 1 // NO_BROWSETABLE is on + ], + + // SQLColAttributes driver specific defines. + // SQLSet/GetDescField driver specific defines. + // Microsoft has 1200 thru 1249 reserved for Microsoft SQL Server driver usage. + // + SQL_CA_SS = // from Odbcss.h + [ + BASE = 1200, // SQL_CA_SS_BASE + + COLUMN_HIDDEN = 1200 + 11, // Column is hidden (FOR BROWSE) + COLUMN_KEY = 1200 + 12, // Column is key column (FOR BROWSE) + VARIANT_TYPE = 1200 + 15, + VARIANT_SQL_TYPE = 1200 + 16, + VARIANT_SERVER_TYPE = 1200 + 17 + + ], + + SQL_SOPT_SS = // from Odbcss.h + [ + BASE = 1225, // SQL_SOPT_SS_BASE + HIDDEN_COLUMNS = 1225 + 2, // Expose FOR BROWSE hidden columns + NOBROWSETABLE = 1225 + 3 // Set NOBROWSETABLE option + ], + + SQL_COMMIT = 0, //Commit + SQL_ROLLBACK = 1, //Abort + + //static public readonly IntPtr SQL_AUTOCOMMIT_OFF = IntPtr.Zero; + //static public readonly IntPtr SQL_AUTOCOMMIT_ON = new IntPtr(1); + + SQL_TRANSACTION = + [ + READ_UNCOMMITTED = 0x00000001, + READ_COMMITTED = 0x00000002, + REPEATABLE_READ = 0x00000004, + SERIALIZABLE = 0x00000008, + SNAPSHOT = 0x00000020 // VSDD 414121: SQL_TXN_SS_SNAPSHOT == 0x20 (sqlncli.h) + ], + + SQL_PARAM = + [ + TYPE_UNKNOWN = 0, // SQL_PARAM_TYPE_UNKNOWN + INPUT = 1, // SQL_PARAM_INPUT + INPUT_OUTPUT = 2, // SQL_PARAM_INPUT_OUTPUT + RESULT_COL = 3, // SQL_RESULT_COL + OUTPUT = 4, // SQL_PARAM_OUTPUT + RETURN_VALUE = 5 // SQL_RETURN_VALUE + ], + + SQL_DESC = + [ + // from sql.h (ODBCVER >= 3.0) + // + COUNT = 1001, + TYPE = 1002, + LENGTH = 1003, + OCTET_LENGTH_PTR = 1004, + PRECISION = 1005, + SCALE = 1006, + DATETIME_INTERVAL_CODE = 1007, + NULLABLE = 1008, + INDICATOR_PTR = 1009, + DATA_PTR = 1010, + NAME = 1011, + UNNAMED = 1012, + OCTET_LENGTH = 1013, + ALLOC_TYPE = 1099, + + // from sqlext.h (ODBCVER >= 3.0) + // + CONCISE_TYPE = SQL_COLUMN[TYPE], + DISPLAY_SIZE = SQL_COLUMN[DISPLAY_SIZE], + UNSIGNED = SQL_COLUMN[UNSIGNED], + UPDATABLE = SQL_COLUMN[UPDATABLE], + AUTO_UNIQUE_VALUE = SQL_COLUMN[AUTO_INCREMENT], + + TYPE_NAME = SQL_COLUMN[TYPE_NAME], + TABLE_NAME = SQL_COLUMN[TABLE_NAME], + SCHEMA_NAME = SQL_COLUMN[OWNER_NAME], + CATALOG_NAME = SQL_COLUMN[QUALIFIER_NAME], + + BASE_COLUMN_NAME = 22, + BASE_TABLE_NAME = 23, + + NUM_PREC_RADIX = 32 + ], + + // ODBC version 2.0 style attributes + // All IdentifierValues are ODBC 1.0 unless marked differently + // + SQL_COLUMN = + [ + COUNT = 0, + NAME = 1, + TYPE = 2, + LENGTH = 3, + PRECISION = 4, + SCALE = 5, + DISPLAY_SIZE = 6, + NULLABLE = 7, + UNSIGNED = 8, + MONEY = 9, + UPDATABLE = 10, + AUTO_INCREMENT = 11, + CASE_SENSITIVE = 12, + SEARCHABLE = 13, + TYPE_NAME = 14, + TABLE_NAME = 15, // (ODBC 2.0) + OWNER_NAME = 16, // (ODBC 2.0) + QUALIFIER_NAME = 17, // (ODBC 2.0) + LABEL = 18 + ], + + // values from sqlext.h + SQL_SQL92_RELATIONAL_JOIN_OPERATORS = + [ + CORRESPONDING_CLAUSE = 0x00000001, // SQL_SRJO_CORRESPONDING_CLAUSE + CROSS_JOIN = 0x00000002, // SQL_SRJO_CROSS_JOIN + EXCEPT_JOIN = 0x00000004, // SQL_SRJO_EXCEPT_JOIN + FULL_OUTER_JOIN = 0x00000008, // SQL_SRJO_FULL_OUTER_JOIN + INNER_JOIN = 0x00000010, // SQL_SRJO_INNER_JOIN + INTERSECT_JOIN = 0x00000020, // SQL_SRJO_INTERSECT_JOIN + LEFT_OUTER_JOIN = 0x00000040, // SQL_SRJO_LEFT_OUTER_JOIN + NATURAL_JOIN = 0x00000080, // SQL_SRJO_NATURAL_JOIN + RIGHT_OUTER_JOIN = 0x00000100, // SQL_SRJO_RIGHT_OUTER_JOIN + UNION_JOIN = 0x00000200 // SQL_SRJO_UNION_JOIN + ], + + // values from sqlext.h + SQL_QU = + [ + SQL_QU_DML_STATEMENTS = 0x00000001, + SQL_QU_PROCEDURE_INVOCATION = 0x00000002, + SQL_QU_TABLE_DEFINITION = 0x00000004, + SQL_QU_INDEX_DEFINITION = 0x00000008, + SQL_QU_PRIVILEGE_DEFINITION = 0x00000010 + ], + + // values from sql.h + SQL_OJ_CAPABILITIES = + [ + LEFT = 0x00000001, // SQL_OJ_LEFT + RIGHT = 0x00000002, // SQL_OJ_RIGHT + FULL = 0x00000004, // SQL_OJ_FULL + NESTED = 0x00000008, // SQL_OJ_NESTED + NOT_ORDERED = 0x00000010, // SQL_OJ_NOT_ORDERED + INNER = 0x00000020, // SQL_OJ_INNER + ALL_COMPARISON_OPS = 0x00000040 //SQL_OJ_ALLCOMPARISION+OPS + ], + + SQL_UPDATABLE = + [ + READONLY = 0, // SQL_ATTR_READ_ONLY + WRITE = 1, // SQL_ATTR_WRITE + READWRITE_UNKNOWN = 2 // SQL_ATTR_READWRITE_UNKNOWN + ], + + SQL_IDENTIFIER_CASE = + [ + UPPER = 1, // SQL_IC_UPPER + LOWER = 2, // SQL_IC_LOWER + SENSITIVE = 3, // SQL_IC_SENSITIVE + MIXED = 4 // SQL_IC_MIXED + ], + + // Uniqueness parameter in the SQLStatistics function + SQL_INDEX = + [ + UNIQUE = 0, + ALL = 1 + ], + + // Reserved parameter in the SQLStatistics function + SQL_STATISTICS_RESERVED = + [ + QUICK = 0, // SQL_QUICK + ENSURE = 1 // SQL_ENSURE + ], + + // Identifier type parameter in the SQLSpecialColumns function + SQL_SPECIALCOLS = + [ + BEST_ROWID = 1, // SQL_BEST_ROWID + ROWVER = 2 // SQL_ROWVER + ], + + // Scope parameter in the SQLSpecialColumns function + SQL_SCOPE = + [ + CURROW = 0, // SQL_SCOPE_CURROW + TRANSACTION = 1, // SQL_SCOPE_TRANSACTION + SESSION = 2 // SQL_SCOPE_SESSION + ], + + SQL_NULLABILITY = + [ + NO_NULLS = 0, // SQL_NO_NULLS + NULLABLE = 1, // SQL_NULLABLE + UNKNOWN = 2 // SQL_NULLABLE_UNKNOWN + ], + + SQL_SEARCHABLE = + [ + UNSEARCHABLE = 0, // SQL_UNSEARCHABLE + LIKE_ONLY = 1, // SQL_LIKE_ONLY + ALL_EXCEPT_LIKE = 2, // SQL_ALL_EXCEPT_LIKE + SEARCHABLE = 3 // SQL_SEARCHABLE + ], + + SQL_UNNAMED = + [ + NAMED = 0, // SQL_NAMED + UNNAMED = 1 // SQL_UNNAMED + ], + // todo:move + // internal constants + // not odbc specific + // + HANDLER = + [ + IGNORE = 0x00000000, + THROW = 0x00000001 + ], + + // values for SQLStatistics TYPE column + SQL_STATISTICSTYPE = + [ + TABLE_STAT = 0, // TABLE Statistics + INDEX_CLUSTERED = 1, // CLUSTERED index statistics + INDEX_HASHED = 2, // HASHED index statistics + INDEX_OTHER = 3 // OTHER index statistics + ], + + // values for SQLProcedures PROCEDURE_TYPE column + SQL_PROCEDURETYPE = + [ + UNKNOWN = 0, // procedure is of unknow type + PROCEDURE = 1, // procedure is a procedure + FUNCTION = 2 // procedure is a function + ], + + // private constants + // to define data types (see below) + // + SIGNED_OFFSET = -20, // SQL_SIGNED_OFFSET + UNSIGNED_OFFSET = -22, // SQL_UNSIGNED_OFFSET + + // C Data Types + SQL_C = + [ + CHAR = 1, + WCHAR = -8, + SLONG = 4 + SIGNED_OFFSET, + ULONG = 4 + UNSIGNED_OFFSET, + SSHORT = 5 + SIGNED_OFFSET, + USHORT = 5 + UNSIGNED_OFFSET, + FLOAT = 7, + DOUBLE = 8, + BIT = -7, + STINYINT = -6 + SIGNED_OFFSET, + UTINYINT = -6 + UNSIGNED_OFFSET, + SBIGINT = -5 + SIGNED_OFFSET, + UBIGINT = -5 + UNSIGNED_OFFSET, + BINARY = -2, + TIMESTAMP = 11, + + TYPE_DATE = 91, + TYPE_TIME = 92, + TYPE_TIMESTAMP = 93, + + NUMERIC = 2, + GUID = -11, + DEFAULT = 99, + ARD_TYPE = -99 + ], + + // SQL Data Types + SQL_TYPE = + [ + // Base data types (sql.h) + UNKNOWN = 0, + NULL = 0, + CHAR = 1, + NUMERIC = 2, + DECIMAL = 3, + INTEGER = 4, + SMALLINT = 5, + FLOAT = 6, + REAL = 7, + DOUBLE = 8, + DATETIME = 9, // V3 Only + VARCHAR = 12, + + // Unicode types (sqlucode.h) + WCHAR = -8, + WVARCHAR = -9, + WLONGVARCHAR = -10, + + // Extended data types (sqlext.h) + INTERVAL = 10, // V3 Only + TIME = 10, + TIMESTAMP = 11, + LONGVARCHAR = -1, + BINARY = -2, + VARBINARY = -3, + LONGVARBINARY = -4, + BIGINT = -5, + TINYINT = -6, + BIT = -7, + GUID = -11, // V3 Only + + // One-parameter shortcuts for date/time data types. + TYPE_DATE = 91, + TYPE_TIME = 92, + TYPE_TIMESTAMP = 93, + + // SQL Server Types -150 to -159 (sqlncli.h) + SS_VARIANT = -150, + SS_UDT = -151, + SS_XML = -152, + SS_TABLE = -153, + SS_TIME2 = -154, + SS_TIMESTAMPOFFSET = -155 + ], + + //SQL_ALL_TYPES = 0, + //static public readonly IntPtr SQL_HANDLE_NULL = IntPtr.Zero; + + SQL_LENGTH = + [ + SQL_IGNORE = -6, + SQL_DEFAULT_PARAM = -5, + SQL_NO_TOTAL = -4, + SQL_NTS = -3, + SQL_DATA_AT_EXEC = -2, + SQL_NULL_DATA = -1 + ], + + SQL_DEFAULT_PARAM = -5, + + // column ordinals for SQLProcedureColumns result set + // this column ordinals are not defined in any c/c++ header but in the ODBC Programmer's Reference under SQLProcedureColumns + // + COLUMN_NAME = 4, + COLUMN_TYPE = 5, + DATA_TYPE = 6, + COLUMN_SIZE = 8, + DECIMAL_DIGITS = 10, + NUM_PREC_RADIX = 11, + + SQL_ATTR = + [ + ODBC_VERSION = 200, + CONNECTION_POOLING = 201, + AUTOCOMMIT = 102, + TXN_ISOLATION = 108, + CURRENT_CATALOG = 109, + LOGIN_TIMEOUT = 103, + QUERY_TIMEOUT = 0, + CONNECTION_DEAD = 1209, + + SQL_COPT_SS_BASE = 1200, + SQL_COPT_SS_ENLIST_IN_DTC = (1200 + 7), + SQL_COPT_SS_TXN_ISOLATION = (1200 + 27), + + MAX_LENGTH = 3, + ROW_BIND_TYPE = 5, + CURSOR_TYPE = 6, + RETRIEVE_DATA = 11, + ROW_STATUS_PTR = 25, + ROWS_FETCHED_PTR = 26, + ROW_ARRAY_SIZE = 27, + + // ODBC 3.0 + APP_ROW_DESC = 10010, + APP_PARAM_DESC = 10011, + IMP_ROW_DESC = 10012, + IMP_PARAM_DESC = 10013, + METADATA_ID = 10014, + + // ODBC 4.0 + PRIVATE_DRIVER_LOCATION = 204 + ], + + SQL_RD = + [ + OFF = 0, + ON = 1 + ], + + SQL_GD = + [ + //None = 0, + ANY_COLUMN = 1, + ANY_ORDER = 2, + BLOCK = 4, + BOUND = 8, + OUTPUT_PARAMS = 16 + ], + + //SQLGetInfo +/* + SQL_INFO = + [ + SQL_ACTIVE_CONNECTIONS = 0, + SQL_MAX_DRIVER_CONNECTIONS = 0, + SQL_MAX_CONCURRENT_ACTIVITIES = 1, + SQL_ACTIVE_STATEMENTS = 1, + SQL_DATA_SOURCE_NAME = 2, + SQL_DRIVER_HDBC, + SQL_DRIVER_HENV, + SQL_DRIVER_HSTMT, + SQL_DRIVER_NAME, + SQL_DRIVER_VER, + SQL_FETCH_DIRECTION, + SQL_ODBC_API_CONFORMANCE, + SQL_ODBC_VER, + SQL_ROW_UPDATES, + SQL_ODBC_SAG_CLI_CONFORMANCE, + SQL_SERVER_NAME, + SQL_SEARCH_PATTERN_ESCAPE, + SQL_ODBC_SQL_CONFORMANCE, + + SQL_DATABASE_NAME, + SQL_DBMS_NAME, + SQL_DBMS_VER, + + SQL_ACCESSIBLE_TABLES, + SQL_ACCESSIBLE_PROCEDURES, + SQL_PROCEDURES, + SQL_CONCAT_NULL_BEHAVIOR, + SQL_CURSOR_COMMIT_BEHAVIOR, + SQL_CURSOR_ROLLBACK_BEHAVIOR, + SQL_DATA_SOURCE_READ_ONLY, + SQL_DEFAULT_TXN_ISOLATION, + SQL_EXPRESSIONS_IN_ORDERBY, + SQL_IDENTIFIER_CASE, + SQL_IDENTIFIER_QUOTE_CHAR, + SQL_MAX_COLUMN_NAME_LEN, + SQL_MAX_CURSOR_NAME_LEN, + SQL_MAX_OWNER_NAME_LEN, + SQL_MAX_SCHEMA_NAME_LEN = 32, + SQL_MAX_PROCEDURE_NAME_LEN, + SQL_MAX_QUALIFIER_NAME_LEN, + SQL_MAX_CATALOG_NAME_LEN = 34, + SQL_MAX_TABLE_NAME_LEN, + SQL_MULT_RESULT_SETS, + SQL_MULTIPLE_ACTIVE_TXN, + SQL_OUTER_JOINS, + SQL_SCHEMA_TERM, + SQL_PROCEDURE_TERM, + SQL_CATALOG_NAME_SEPARATOR, + SQL_CATALOG_TERM, + SQL_SCROLL_CONCURRENCY, + SQL_SCROLL_OPTIONS, + SQL_TABLE_TERM, + SQL_TXN_CAPABLE, + SQL_USER_NAME, + + SQL_CONVERT_FUNCTIONS, + SQL_NUMERIC_FUNCTIONS, + SQL_STRING_FUNCTIONS, + SQL_SYSTEM_FUNCTIONS, + SQL_TIMEDATE_FUNCTIONS, + + SQL_CONVERT_BIGINT, + SQL_CONVERT_BINARY, + SQL_CONVERT_BIT, + SQL_CONVERT_CHAR, + SQL_CONVERT_DATE, + SQL_CONVERT_DECIMAL, + SQL_CONVERT_DOUBLE, + SQL_CONVERT_FLOAT, + SQL_CONVERT_INTEGER, + SQL_CONVERT_LONGVARCHAR, + SQL_CONVERT_NUMERIC, + SQL_CONVERT_REAL, + SQL_CONVERT_SMALLINT, + SQL_CONVERT_TIME, + SQL_CONVERT_TIMESTAMP, + SQL_CONVERT_TINYINT, + SQL_CONVERT_VARBINARY, + SQL_CONVERT_VARCHAR, + SQL_CONVERT_LONGVARBINARY, + + SQL_TXN_ISOLATION_OPTION, + SQL_ODBC_SQL_OPT_IEF, + SQL_INTEGRITY = 73, + SQL_CORRELATION_NAME, + SQL_NON_NULLABLE_COLUMNS, + SQL_DRIVER_HLIB, + SQL_DRIVER_ODBC_VER, + SQL_LOCK_TYPES, + SQL_POS_OPERATIONS, + SQL_POSITIONED_STATEMENTS, + SQL_GETDATA_EXTENSIONS, + SQL_BOOKMARK_PERSISTENCE, + SQL_STATIC_SENSITIVITY, + SQL_FILE_USAGE, + SQL_NULL_COLLATION, + SQL_ALTER_TABLE, + SQL_COLUMN_ALIAS, + SQL_GROUP_BY, + SQL_KEYWORDS, + SQL_ORDER_BY_COLUMNS_IN_SELECT, + SQL_SCHEMA_USAGE, + SQL_CATALOG_USAGE, + SQL_QUOTED_IDENTIFIER_CASE, + SQL_SPECIAL_CHARACTERS, + SQL_SUBQUERIES, + SQL_UNION_STATEMENT, + SQL_MAX_COLUMNS_IN_GROUP_BY, + SQL_MAX_COLUMNS_IN_INDEX, + SQL_MAX_COLUMNS_IN_ORDER_BY, + SQL_MAX_COLUMNS_IN_SELECT, + SQL_MAX_COLUMNS_IN_TABLE, + SQL_MAX_INDEX_SIZE, + SQL_MAX_ROW_SIZE_INCLUDES_LONG, + SQL_MAX_ROW_SIZE, + SQL_MAX_STATEMENT_LEN, + SQL_MAX_TABLES_IN_SELECT, + SQL_MAX_USER_NAME_LEN, + SQL_MAX_CHAR_LITERAL_LEN, + SQL_TIMEDATE_ADD_INTERVALS, + SQL_TIMEDATE_DIFF_INTERVALS, + SQL_NEED_LONG_DATA_LEN, + SQL_MAX_BINARY_LITERAL_LEN, + SQL_LIKE_ESCAPE_CLAUSE, + SQL_CATALOG_LOCATION, + SQL_OJ_CAPABILITIES, + + SQL_ACTIVE_ENVIRONMENTS, + SQL_ALTER_DOMAIN, + SQL_SQL_CONFORMANCE, + SQL_DATETIME_LITERALS, + SQL_BATCH_ROW_COUNT, + SQL_BATCH_SUPPORT, + SQL_CONVERT_WCHAR, + SQL_CONVERT_INTERVAL_DAY_TIME, + SQL_CONVERT_INTERVAL_YEAR_MONTH, + SQL_CONVERT_WLONGVARCHAR, + SQL_CONVERT_WVARCHAR, + SQL_CREATE_ASSERTION, + SQL_CREATE_CHARACTER_SET, + SQL_CREATE_COLLATION, + SQL_CREATE_DOMAIN, + SQL_CREATE_SCHEMA, + SQL_CREATE_TABLE, + SQL_CREATE_TRANSLATION, + SQL_CREATE_VIEW, + SQL_DRIVER_HDESC, + SQL_DROP_ASSERTION, + SQL_DROP_CHARACTER_SET, + SQL_DROP_COLLATION, + SQL_DROP_DOMAIN, + SQL_DROP_SCHEMA, + SQL_DROP_TABLE, + SQL_DROP_TRANSLATION, + SQL_DROP_VIEW, + SQL_DYNAMIC_CURSOR_ATTRIBUTES1, + SQL_DYNAMIC_CURSOR_ATTRIBUTES2, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, + SQL_INDEX_KEYWORDS, + SQL_INFO_SCHEMA_VIEWS, + SQL_KEYSET_CURSOR_ATTRIBUTES1, + SQL_KEYSET_CURSOR_ATTRIBUTES2, + SQL_ODBC_INTERFACE_CONFORMANCE, + SQL_PARAM_ARRAY_ROW_COUNTS, + SQL_PARAM_ARRAY_SELECTS, + SQL_SQL92_DATETIME_FUNCTIONS, + SQL_SQL92_FOREIGN_KEY_DELETE_RULE, + SQL_SQL92_FOREIGN_KEY_UPDATE_RULE, + SQL_SQL92_GRANT, + SQL_SQL92_NUMERIC_VALUE_FUNCTIONS, + SQL_SQL92_PREDICATES, + SQL_SQL92_RELATIONAL_JOIN_OPERATORS, + SQL_SQL92_REVOKE, + SQL_SQL92_ROW_VALUE_CONSTRUCTOR, + SQL_SQL92_STRING_FUNCTIONS, + SQL_SQL92_VALUE_EXPRESSIONS, + SQL_STANDARD_CLI_CONFORMANCE, + SQL_STATIC_CURSOR_ATTRIBUTES1, + SQL_STATIC_CURSOR_ATTRIBUTES2, + SQL_AGGREGATE_FUNCTIONS, + SQL_DDL_INDEX, + SQL_DM_VER, + SQL_INSERT_STATEMENT, + SQL_CONVERT_GUID, + + SQL_XOPEN_CLI_YEAR = 10000, + SQL_CURSOR_SENSITIVITY, + SQL_DESCRIBE_PARAMETER, + SQL_CATALOG_NAME, + SQL_COLLATION_SEQ, + SQL_MAX_IDENTIFIER_LEN, + SQL_ASYNC_MODE = 10021, + SQL_MAX_ASYNC_CONCURRENT_STATEMENTS, + + SQL_DTC_TRANSITION_COST = 1750, + ], +*/ + SQL_OAC = + [ + SQL_OAC_None = 0x0000, + SQL_OAC_LEVEL1 = 0x0001, + SQL_OAC_LEVEL2 = 0x0002 + ], + + SQL_OSC = + [ + SQL_OSC_MINIMUM = 0x0000, + SQL_OSC_CORE = 0x0001, + SQL_OSC_EXTENDED = 0x0002 + ], + + SQL_SCC = + [ + SQL_SCC_XOPEN_CLI_VERSION1 = 0x00000001, + SQL_SCC_ISO92_CLI = 0x00000002 + ], + + SQL_SVE = + [ + SQL_SVE_CASE = 0x00000001, + SQL_SVE_CAST = 0x00000002, + SQL_SVE_COALESCE = 0x00000004, + SQL_SVE_NULLIF = 0x00000008 + ], + + SQL_SSF = + [ + SQL_SSF_CONVERT = 0x00000001, + SQL_SSF_LOWER = 0x00000002, + SQL_SSF_UPPER = 0x00000004, + SQL_SSF_SUBSTRING = 0x00000008, + SQL_SSF_TRANSLATE = 0x00000010, + SQL_SSF_TRIM_BOTH = 0x00000020, + SQL_SSF_TRIM_LEADING = 0x00000040, + SQL_SSF_TRIM_TRAILING = 0x00000080 + ], + + SQL_SP = + [ + //None = 0, + + SQL_SP_EXISTS = 0x00000001, + SQL_SP_ISNOTNULL = 0x00000002, + SQL_SP_ISNULL = 0x00000004, + SQL_SP_MATCH_FULL = 0x00000008, + SQL_SP_MATCH_PARTIAL = 0x00000010, + SQL_SP_MATCH_UNIQUE_FULL = 0x00000020, + SQL_SP_MATCH_UNIQUE_PARTIAL = 0x00000040, + SQL_SP_OVERLAPS = 0x00000080, + SQL_SP_UNIQUE = 0x00000100, + SQL_SP_LIKE = 0x00000200, + SQL_SP_IN = 0x00000400, + SQL_SP_BETWEEN = 0x00000800, + SQL_SP_COMPARISON = 0x00001000, + SQL_SP_QUANTIFIED_COMPARISON = 0x00002000, + + All = 0x0000FFFF + ], + + SQL_OIC = + [ + SQL_OIC_CORE = 1, + SQL_OIC_LEVEL1 = 2, + SQL_OIC_LEVEL2 = 3 + ], + + SQL_USAGE = + [ + SQL_U_DML_STATEMENTS = 0x00000001, + SQL_U_PROCEDURE_INVOCATION = 0x00000002, + SQL_U_TABLE_DEFINITION = 0x00000004, + SQL_U_INDEX_DEFINITION = 0x00000008, + SQL_U_PRIVILEGE_DEFINITION = 0x00000010 + ], + + SQL_GB = + [ + + SQL_GB_NOT_SUPPORTED = 0, + SQL_GB_GROUP_BY_EQUALS_SELECT = 1, + SQL_GB_GROUP_BY_CONTAINS_SELECT = 2, + SQL_GB_NO_RELATION = 3, + SQL_GB_COLLATE = 4 + ], + + SQL_NC = + [ + SQL_NC_END = 0, + SQL_NC_HIGH = 1, + SQL_NC_LOW = 2, + SQL_NC_START = 3 + ], + + SQL_CN = + [ + SQL_CN_None = 0, + SQL_CN_DIFFERENT = 1, + SQL_CN_ANY = 2 + ], + + SQL_NNC = + [ + SQL_NNC_NULL = 0, + SQL_NNC_NON_NULL = 1 + ], + + SQL_CB = + [ + SQL_CB_NULL = 0, + SQL_CB_NON_NULL = 1 + ], + + SQL_FD_FETCH = + [ + SQL_FD_FETCH_NEXT = 0x00000001, + SQL_FD_FETCH_FIRST = 0x00000002, + SQL_FD_FETCH_LAST = 0x00000004, + SQL_FD_FETCH_PRIOR = 0x00000008, + SQL_FD_FETCH_ABSOLUTE = 0x00000010, + SQL_FD_FETCH_RELATIVE = 0x00000020, + SQL_FD_FETCH_BOOKMARK = 0x00000080 + ], + + SQL_SQ = + [ + SQL_SQ_COMPARISON = 0x00000001, + SQL_SQ_EXISTS = 0x00000002, + SQL_SQ_IN = 0x00000004, + SQL_SQ_QUANTIFIED = 0x00000008, + SQL_SQ_CORRELATED_SUBQUERIES = 0x00000010 + ], + + SQL_U = + [ + SQL_U_UNION = 0x00000001, + SQL_U_UNION_ALL = 0x00000002 + ], + + SQL_BP = + [ + SQL_BP_CLOSE = 0x00000001, + SQL_BP_DELETE = 0x00000002, + SQL_BP_DROP = 0x00000004, + SQL_BP_TRANSACTION = 0x00000008, + SQL_BP_UPDATE = 0x00000010, + SQL_BP_OTHER_HSTMT = 0x00000020, + SQL_BP_SCROLL = 0x00000040 + ], + + SQL_QL = + [ + SQL_QL_START = 0x0001, + SQL_QL_END = 0x0002 + ], + + SQL_OJ = + [ + SQL_OJ_LEFT = 0x00000001, + SQL_OJ_RIGHT = 0x00000002, + SQL_OJ_FULL = 0x00000004, + SQL_OJ_NESTED = 0x00000008, + SQL_OJ_NOT_ORDERED = 0x00000010, + SQL_OJ_INNER = 0x00000020, + SQL_OJ_ALL_COMPARISON_OPS = 0x00000040 + ], + + SQL_FN_CVT = + [ + //None = 0, + + SQL_FN_CVT_CONVERT = 0x00000001, + SQL_FN_CVT_CAST = 0x00000002 + ], + + SQL_FN_NUM = + [ + //None = 0, + + SQL_FN_NUM_ABS = 0x00000001, + SQL_FN_NUM_ACOS = 0x00000002, + SQL_FN_NUM_ASIN = 0x00000004, + SQL_FN_NUM_ATAN = 0x00000008, + SQL_FN_NUM_ATAN2 = 0x00000010, + SQL_FN_NUM_CEILING = 0x00000020, + SQL_FN_NUM_COS = 0x00000040, + SQL_FN_NUM_COT = 0x00000080, + SQL_FN_NUM_EXP = 0x00000100, + SQL_FN_NUM_FLOOR = 0x00000200, + SQL_FN_NUM_LOG = 0x00000400, + SQL_FN_NUM_MOD = 0x00000800, + SQL_FN_NUM_SIGN = 0x00001000, + SQL_FN_NUM_SIN = 0x00002000, + SQL_FN_NUM_SQRT = 0x00004000, + SQL_FN_NUM_TAN = 0x00008000, + SQL_FN_NUM_PI = 0x00010000, + SQL_FN_NUM_RAND = 0x00020000, + SQL_FN_NUM_DEGREES = 0x00040000, + SQL_FN_NUM_LOG10 = 0x00080000, + SQL_FN_NUM_POWER = 0x00100000, + SQL_FN_NUM_RADIANS = 0x00200000, + SQL_FN_NUM_ROUND = 0x00400000, + SQL_FN_NUM_TRUNCATE = 0x00800000 + ], + + SQL_SNVF = + [ + SQL_SNVF_BIT_LENGTH = 0x00000001, + SQL_SNVF_CHAR_LENGTH = 0x00000002, + SQL_SNVF_CHARACTER_LENGTH = 0x00000004, + SQL_SNVF_EXTRACT = 0x00000008, + SQL_SNVF_OCTET_LENGTH = 0x00000010, + SQL_SNVF_POSITION = 0x00000020 + ], + + SQL_FN_STR = + [ + //None = 0, + + SQL_FN_STR_CONCAT = 0x00000001, + SQL_FN_STR_INSERT = 0x00000002, + SQL_FN_STR_LEFT = 0x00000004, + SQL_FN_STR_LTRIM = 0x00000008, + SQL_FN_STR_LENGTH = 0x00000010, + SQL_FN_STR_LOCATE = 0x00000020, + SQL_FN_STR_LCASE = 0x00000040, + SQL_FN_STR_REPEAT = 0x00000080, + SQL_FN_STR_REPLACE = 0x00000100, + SQL_FN_STR_RIGHT = 0x00000200, + SQL_FN_STR_RTRIM = 0x00000400, + SQL_FN_STR_SUBSTRING = 0x00000800, + SQL_FN_STR_UCASE = 0x00001000, + SQL_FN_STR_ASCII = 0x00002000, + SQL_FN_STR_CHAR = 0x00004000, + SQL_FN_STR_DIFFERENCE = 0x00008000, + SQL_FN_STR_LOCATE_2 = 0x00010000, + SQL_FN_STR_SOUNDEX = 0x00020000, + SQL_FN_STR_SPACE = 0x00040000, + SQL_FN_STR_BIT_LENGTH = 0x00080000, + SQL_FN_STR_CHAR_LENGTH = 0x00100000, + SQL_FN_STR_CHARACTER_LENGTH = 0x00200000, + SQL_FN_STR_OCTET_LENGTH = 0x00400000, + SQL_FN_STR_POSITION = 0x00800000 + ], + + SQL_FN_SYSTEM = + [ + //None = 0, + + SQL_FN_SYS_USERNAME = 0x00000001, + SQL_FN_SYS_DBNAME = 0x00000002, + SQL_FN_SYS_IFNULL = 0x00000004 + ], + + SQL_FN_TD = + [ + //None = 0, + + SQL_FN_TD_NOW = 0x00000001, + SQL_FN_TD_CURDATE = 0x00000002, + SQL_FN_TD_DAYOFMONTH = 0x00000004, + SQL_FN_TD_DAYOFWEEK = 0x00000008, + SQL_FN_TD_DAYOFYEAR = 0x00000010, + SQL_FN_TD_MONTH = 0x00000020, + SQL_FN_TD_QUARTER = 0x00000040, + SQL_FN_TD_WEEK = 0x00000080, + SQL_FN_TD_YEAR = 0x00000100, + SQL_FN_TD_CURTIME = 0x00000200, + SQL_FN_TD_HOUR = 0x00000400, + SQL_FN_TD_MINUTE = 0x00000800, + SQL_FN_TD_SECOND = 0x00001000, + SQL_FN_TD_TIMESTAMPADD = 0x00002000, + SQL_FN_TD_TIMESTAMPDIFF = 0x00004000, + SQL_FN_TD_DAYNAME = 0x00008000, + SQL_FN_TD_MONTHNAME = 0x00010000, + SQL_FN_TD_CURRENT_DATE = 0x00020000, + SQL_FN_TD_CURRENT_TIME = 0x00040000, + SQL_FN_TD_CURRENT_TIMESTAMP = 0x00080000, + SQL_FN_TD_EXTRACT = 0x00100000 + ], + + SQL_SDF = + [ + SQL_SDF_CURRENT_DATE = 0x00000001, + SQL_SDF_CURRENT_TIME = 0x00000002, + SQL_SDF_CURRENT_TIMESTAMP = 0x00000004 + ], + + SQL_TSI = + [ + //None = 0, + + SQL_TSI_FRAC_SECOND = 0x00000001, + SQL_TSI_SECOND = 0x00000002, + SQL_TSI_MINUTE = 0x00000004, + SQL_TSI_HOUR = 0x00000008, + SQL_TSI_DAY = 0x00000010, + SQL_TSI_WEEK = 0x00000020, + SQL_TSI_MONTH = 0x00000040, + SQL_TSI_QUARTER = 0x00000080, + SQL_TSI_YEAR = 0x00000100 + ], + + SQL_AF = + [ + //None = 0, + + SQL_AF_AVG = 0x00000001, + SQL_AF_COUNT = 0x00000002, + SQL_AF_MAX = 0x00000004, + SQL_AF_MIN = 0x00000008, + SQL_AF_SUM = 0x00000010, + SQL_AF_DISTINCT = 0x00000020, + SQL_AF_ALL = 0x00000040, + + All = 0xFF + ], + + SQL_SC = + [ + //None = 0, + + SQL_SC_SQL92_ENTRY = 0x00000001, + SQL_SC_FIPS127_2_TRANSITIONAL = 0x00000002, + SQL_SC_SQL92_INTERMEDIATE = 0x00000004, + SQL_SC_SQL92_FULL = 0x00000008 + ], + + SQL_DL_SQL92 = + [ + SQL_DL_SQL92_DATE = 0x00000001, + SQL_DL_SQL92_TIME = 0x00000002, + SQL_DL_SQL92_TIMESTAMP = 0x00000004, + SQL_DL_SQL92_INTERVAL_YEAR = 0x00000008, + SQL_DL_SQL92_INTERVAL_MONTH = 0x00000010, + SQL_DL_SQL92_INTERVAL_DAY = 0x00000020, + SQL_DL_SQL92_INTERVAL_HOUR = 0x00000040, + SQL_DL_SQL92_INTERVAL_MINUTE = 0x00000080, + SQL_DL_SQL92_INTERVAL_SECOND = 0x00000100, + SQL_DL_SQL92_INTERVAL_YEAR_TO_MONTH = 0x00000200, + SQL_DL_SQL92_INTERVAL_DAY_TO_HOUR = 0x00000400, + SQL_DL_SQL92_INTERVAL_DAY_TO_MINUTE = 0x00000800, + SQL_DL_SQL92_INTERVAL_DAY_TO_SECOND = 0x00001000, + SQL_DL_SQL92_INTERVAL_HOUR_TO_MINUTE = 0x00002000, + SQL_DL_SQL92_INTERVAL_HOUR_TO_SECOND = 0x00004000, + SQL_DL_SQL92_INTERVAL_MINUTE_TO_SECOND = 0x00008000 + ], + + SQL_IK = + [ + SQL_IK_NONE = 0x00000000, + SQL_IK_ASC = 0x00000001, + SQL_IK_DESC = 0x00000002, + SQL_IK_ALL = 0x00000003 //SQL_IK_ASC | SQL_IK_DESC + ], + + SQL_ISV = + [ + SQL_ISV_ASSERTIONS = 0x00000001, + SQL_ISV_CHARACTER_SETS = 0x00000002, + SQL_ISV_CHECK_CONSTRAINTS = 0x00000004, + SQL_ISV_COLLATIONS = 0x00000008, + SQL_ISV_COLUMN_DOMAIN_USAGE = 0x00000010, + SQL_ISV_COLUMN_PRIVILEGES = 0x00000020, + SQL_ISV_COLUMNS = 0x00000040, + SQL_ISV_CONSTRAINT_COLUMN_USAGE = 0x00000080, + SQL_ISV_CONSTRAINT_TABLE_USAGE = 0x00000100, + SQL_ISV_DOMAIN_CONSTRAINTS = 0x00000200, + SQL_ISV_DOMAINS = 0x00000400, + SQL_ISV_KEY_COLUMN_USAGE = 0x00000800, + SQL_ISV_REFERENTIAL_CONSTRAINTS = 0x00001000, + SQL_ISV_SCHEMATA = 0x00002000, + SQL_ISV_SQL_LANGUAGES = 0x00004000, + SQL_ISV_TABLE_CONSTRAINTS = 0x00008000, + SQL_ISV_TABLE_PRIVILEGES = 0x00010000, + SQL_ISV_TABLES = 0x00020000, + SQL_ISV_TRANSLATIONS = 0x00040000, + SQL_ISV_USAGE_PRIVILEGES = 0x00080000, + SQL_ISV_VIEW_COLUMN_USAGE = 0x00100000, + SQL_ISV_VIEW_TABLE_USAGE = 0x00200000, + SQL_ISV_VIEWS = 0x00400000 + ], + + SQL_SRJO = + [ + //None = 0, + + SQL_SRJO_CORRESPONDING_CLAUSE = 0x00000001, + SQL_SRJO_CROSS_JOIN = 0x00000002, + SQL_SRJO_EXCEPT_JOIN = 0x00000004, + SQL_SRJO_FULL_OUTER_JOIN = 0x00000008, + SQL_SRJO_INNER_JOIN = 0x00000010, + SQL_SRJO_INTERSECT_JOIN = 0x00000020, + SQL_SRJO_LEFT_OUTER_JOIN = 0x00000040, + SQL_SRJO_NATURAL_JOIN = 0x00000080, + SQL_SRJO_RIGHT_OUTER_JOIN = 0x00000100, + SQL_SRJO_UNION_JOIN = 0x00000200 + ], + + SQL_SRVC = + [ + SQL_SRVC_VALUE_EXPRESSION = 0x00000001, + SQL_SRVC_NULL = 0x00000002, + SQL_SRVC_DEFAULT = 0x00000004, + SQL_SRVC_ROW_SUBQUERY = 0x00000008 + ], + + //public static readonly int SQL_OV_ODBC3 = 3; + //public const Int32 SQL_NTS = -3; //flags for null-terminated string + + //Pooling + SQL_CP = + [ + OFF = 0, + ONE_PER_DRIVER = 1, + ONE_PER_HENV = 2 + ], + +/* + public const Int32 SQL_CD_TRUE = 1; + public const Int32 SQL_CD_FALSE = 0; + + public const Int32 SQL_DTC_DONE = 0; + public const Int32 SQL_IS_POINTER = -4; + public const Int32 SQL_IS_PTR = 1; +*/ + SQL_DRIVER = + [ + NOPROMPT = 0, + COMPLETE = 1, + PROMPT = 2, + COMPLETE_REQUIRED = 3 + ], + + // Column set for SQLPrimaryKeys + SQL_PRIMARYKEYS = + [ + /* + CATALOGNAME = 1, // TABLE_CAT + SCHEMANAME = 2, // TABLE_SCHEM + TABLENAME = 3, // TABLE_NAME + */ + COLUMNNAME = 4 // COLUMN_NAME + /* + KEY_SEQ = 5, // KEY_SEQ + PKNAME = 6, // PK_NAME + */ + ], + + // Column set for SQLStatistics + SQL_STATISTICS = + [ + /* + CATALOGNAME = 1, // TABLE_CAT + SCHEMANAME = 2, // TABLE_SCHEM + TABLENAME = 3, // TABLE_NAME + NONUNIQUE = 4, // NON_UNIQUE + INDEXQUALIFIER = 5, // INDEX_QUALIFIER + */ + INDEXNAME = 6, // INDEX_NAME + /* + TYPE = 7, // TYPE + */ + ORDINAL_POSITION = 8, // ORDINAL_POSITION + COLUMN_NAME = 9 // COLUMN_NAME + /* + ASC_OR_DESC = 10, // ASC_OR_DESC + CARDINALITY = 11, // CARDINALITY + PAGES = 12, // PAGES + FILTER_CONDITION = 13, // FILTER_CONDITION + */ + ], + + // Column set for SQLSpecialColumns + SQL_SPECIALCOLUMNSET = + [ + /* + SCOPE = 1, // SCOPE + */ + COLUMN_NAME = 2 // COLUMN_NAME + /* + DATA_TYPE = 3, // DATA_TYPE + TYPE_NAME = 4, // TYPE_NAME + COLUMN_SIZE = 5, // COLUMN_SIZE + BUFFER_LENGTH = 6, // BUFFER_LENGTH + DECIMAL_DIGITS = 7, // DECIMAL_DIGITS + PSEUDO_COLUMN = 8, // PSEUDO_COLUMN + */ + ], + + SQL_DIAG = + [ + CURSOR_ROW_COUNT= -1249, + ROW_NUMBER = -1248, + COLUMN_NUMBER = -1247, + RETURNCODE = 1, + NUMBER = 2, + ROW_COUNT = 3, + SQLSTATE = 4, + NATIVE = 5, + MESSAGE_TEXT = 6, + DYNAMIC_FUNCTION = 7, + CLASS_ORIGIN = 8, + SUBCLASS_ORIGIN = 9, + CONNECTION_NAME = 10, + SERVER_NAME = 11, + DYNAMIC_FUNCTION_CODE = 12 + ], + + SQL_SU = + [ + SQL_SU_DML_STATEMENTS = 0x00000001, + SQL_SU_PROCEDURE_INVOCATION = 0x00000002, + SQL_SU_TABLE_DEFINITION = 0x00000004, + SQL_SU_INDEX_DEFINITION = 0x00000008, + SQL_SU_PRIVILEGE_DEFINITION = 0x00000010 + ] +] diff --git a/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.mproj b/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.mproj index 0987401eb4..543b5652c8 100644 --- a/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.mproj +++ b/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.mproj @@ -66,6 +66,12 @@ Code + + Content + + + Content + Code diff --git a/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.pq b/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.pq index 6b9abdba5b..f1af5f74b6 100644 --- a/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.pq +++ b/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.pq @@ -1,6 +1,12 @@ -// This file contains your Data Connector logic +// This file contains Data Connector logic section OdfeSqlOdbcPBIConnector; +// When set to true, additional trace information will be written out to the User log. +// This should be set to false before release. Tracing is done through a call to +// Diagnostics.LogValue(). When EnableTraceOutput is set to false, the call becomes a +// no-op and simply returns the original value. +EnableTraceOutput = true; + [DataSource.Kind="OdfeSqlOdbcPBIConnector", Publish="OdfeSqlOdbcPBIConnector.Publish"] shared OdfeSqlOdbcPBIConnector.Contents = Value.ReplaceType(OdfeSqlOdbcPBIConnectorImpl, OdfeSqlOdbcPBIConnectorType); @@ -8,12 +14,12 @@ shared OdfeSqlOdbcPBIConnector.Contents = Value.ReplaceType(OdfeSqlOdbcPBIConnec OdfeSqlOdbcPBIConnectorType = type function ( Host as (type text meta [ Documentation.FieldCaption = "Host", - Documentation.FieldDescription = "The hostname of the Open Distro For Elasticsearc server.", + Documentation.FieldDescription = "The hostname of the Open Distro For Elasticsearch server.", Documentation.SampleValues = { "localhost" } ]), - Port as (type number meta [ + optional Port as (type number meta [ Documentation.FieldCaption = "Port", - Documentation.FieldDescription = "The port the Open Distro For Elasticsearc server is running on.", + Documentation.FieldDescription = "The port of the Open Distro For Elasticsearch server is running on.", Documentation.SampleValues = { 9200 } ]) ) @@ -21,22 +27,177 @@ OdfeSqlOdbcPBIConnectorType = type function ( Documentation.Name = "Open Distro For Elasticsearch" ]; -OdfeSqlOdbcPBIConnectorImpl = (Host as text, Port as number) as table => +OdfeSqlOdbcPBIConnectorImpl = (Host as text, optional Port as number) as table => let + Credential = Extension.CurrentCredential(), + AuthenticationMode = Credential[AuthenticationKind], + + // Sets connection string properties for authentication. + CredentialConnectionString = + if AuthenticationMode = "UsernamePassword" then + [ + Auth = "BASIC", + UID = Credential[Username], + PWD = Credential[Password] + ] + else if AuthenticationMode = "Key" then + [ + Auth = "AWS_SIGV4", + Region = Credential[Key] + ] + else + [ + Auth = "NONE" + ], + + // Sets connection string properties for encrypted connections. + EncryptedConnectionString = + if Credential[EncryptConnection] = null or Credential[EncryptConnection] = true then + [ + UseSSL = 1 + ] + else + [ + UseSSL = 0 + ], + + // Set host & port in connection string. + // Do not include port in connection string for aws server connection. + Server = + if Port <> null then + [ + Host = Host, + Port = Port + ] + else + [ + Host = Host + ], + ConnectionString = [ - Driver = "ODFE SQL ODBC Driver", - Host = Host, - Port = Port + Driver = "ODFE SQL ODBC Driver" ], - OdbcDatasource = Odbc.DataSource(ConnectionString) + + SQLGetTypeInfo = (types) => + if (EnableTraceOutput <> true) then types else + let + // Outputting the entire table might be too large, and result in the value being truncated. + // We can output a row at a time instead with Table.TransformRows() + rows = Table.TransformRows(types, each Diagnostics.LogValue("SQLGetTypeInfo " & _[TYPE_NAME], _)), + toTable = Table.FromRecords(rows) + in + Value.ReplaceType(toTable, Value.Type(types)), + + // SQLColumns is a function handler that receives the results of an ODBC call to SQLColumns(). + SQLColumns = (catalogName, schemaName, tableName, columnName, source) => + if (EnableTraceOutput <> true) then source else + // the if statement conditions will force the values to evaluated/written to diagnostics + if (Diagnostics.LogValue("SQLColumns.TableName", tableName) <> "***" and Diagnostics.LogValue("SQLColumns.ColumnName", columnName) <> "***") then + let + // Outputting the entire table might be too large, and result in the value being truncated. + // We can output a row at a time instead with Table.TransformRows() + rows = Table.TransformRows(source, each Diagnostics.LogValue("SQLColumns", _)), + toTable = Table.FromRecords(rows) + in + Value.ReplaceType(toTable, Value.Type(source)) + else + source, + + // Add support for `LIMIT` and `OFFSET` clauses (rather than `TOP`) + AstVisitor = [ + // format is "LIMIT [,]" - ex. LIMIT 2,10 or LIMIT 10 + LimitClause = (skip, take) => + if (take = null) then + ... + else + let + skip = + if (skip = null or skip = 0) then + "" + else + Number.ToText(skip) & "," + in + [ + Text = Text.Format("LIMIT #{0}#{1}", { skip, take }), + Location = "AfterQuerySpecification" + ] + ], + + OdbcDatasource = Odbc.DataSource(ConnectionString & Server & CredentialConnectionString & EncryptedConnectionString, [ + // Do not view the tables grouped by their schema names. + HierarchicalNavigation = false, + // Prevents execution of native SQL statements. Extensions should set this to true. + HideNativeQuery = true, + // Allows upconversion of numeric types + SoftNumbers = true, + // Allow upconversion / resizing of numeric and string types + TolerateConcatOverflow = true, + // Enables connection pooling via the system ODBC manager + ClientConnectionPooling = true, + + // These values should be set by previous steps + AstVisitor = AstVisitor, + SQLColumns = SQLColumns, + SQLGetTypeInfo = SQLGetTypeInfo, + + OnError = OnOdbcError, + + // Connection string properties used for encrypted connections. + CredentialConnectionString = EncryptedConnectionString + ]) in OdbcDatasource; +// Handles ODBC errors. +OnOdbcError = (errorRecord as record) => + let + ErrorMessage = errorRecord[Message], + ConnectionHostPort = errorRecord[Detail][DataSourcePath], + + IsDriverNotInstalled = Text.Contains(ErrorMessage, "doesn't correspond to an installed ODBC driver"), + + OdbcError = errorRecord[Detail][OdbcErrors]{0}, + OdbcErrorCode = OdbcError[NativeError], + + // Failed to connect to given host + IsHostUnreachable = + OdbcErrorCode = 202 + in + if IsDriverNotInstalled then + error Error.Record("", "The Open Distro For Elasticsearch SQL ODBC driver is not installed. Please install the driver") + else if IsHostUnreachable then + error Error.Record("", "Couldn't reach server. Please double-check the host, port and auth.") + else + error errorRecord; + // Data Source Kind description OdfeSqlOdbcPBIConnector = [ + // Required for use with Power BI Service. + TestConnection = (dataSourcePath) => + let + json = Json.Document(dataSourcePath), + Host = json[Host], + Port = json[Port] + in + { "OdfeSqlOdbcPBIConnector.Contents", Host, Port }, + + // Authentication modes Authentication = [ - Implicit = [] + Implicit = [ + Label = "NONE" + ], + UsernamePassword = [ + Label = "BASIC" + ], + Key = [ + Label = "AWS_SIGV4", + KeyLabel = "Region" + ] ], + + // Enable Encryption + SupportsEncryption = true, + Label = Extension.LoadString("DataSourceLabel") ]; @@ -46,6 +207,7 @@ OdfeSqlOdbcPBIConnector.Publish = [ Category = "Other", ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") }, LearnMoreUrl = "https://opendistro.github.io/for-elasticsearch/", + SupportsDirectQuery = true, SourceImage = OdfeSqlOdbcPBIConnector.Icons, SourceTypeImage = OdfeSqlOdbcPBIConnector.Icons ]; @@ -54,3 +216,18 @@ OdfeSqlOdbcPBIConnector.Icons = [ Icon16 = { Extension.Contents("OdfeSqlOdbcPBIConnector16.png"), Extension.Contents("OdfeSqlOdbcPBIConnector20.png"), Extension.Contents("OdfeSqlOdbcPBIConnector24.png"), Extension.Contents("OdfeSqlOdbcPBIConnector32.png") }, Icon32 = { Extension.Contents("OdfeSqlOdbcPBIConnector32.png"), Extension.Contents("OdfeSqlOdbcPBIConnector40.png"), Extension.Contents("OdfeSqlOdbcPBIConnector48.png"), Extension.Contents("OdfeSqlOdbcPBIConnector64.png") } ]; + +// Load common library functions +Extension.LoadFunction = (name as text) => + let + binary = Extension.Contents(name), + asText = Text.FromBinary(binary) + in + Expression.Evaluate(asText, #shared); + +// Diagnostics module contains multiple functions. . +Diagnostics = Extension.LoadFunction("Diagnostics.pqm"); +Diagnostics.LogValue = if (EnableTraceOutput) then Diagnostics[LogValue] else (prefix, value) => value; + +// OdbcConstants contains numeric constants from the ODBC header files, and helper function to create bitfield values. +ODBC = Extension.LoadFunction("OdbcConstants.pqm"); diff --git a/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.query.pq b/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.query.pq index 7b2a564376..def55af3e2 100644 --- a/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.query.pq +++ b/sql-odbc/src/PowerBIConnector/OdfeSqlOdbcPBIConnector.query.pq @@ -1,5 +1,310 @@ -// Use this file to write queries to test your data connector -let - result = OdfeSqlOdbcPBIConnector.Contents() -in - result \ No newline at end of file +// This file contains queries to test your data connector +section OdfeSqlOdbcPBIConnector.UnitTests; + +shared MyExtension.UnitTest = +[ + // Common variables for all tests + Host = "localhost", + Port = 9200, + + facts = + { + Fact("Connection Test", + 7, + let + Source = OdfeSqlOdbcPBIConnector.Contents(Host,Port), + no_of_columns = Table.ColumnCount(Source) + in + no_of_columns + ) + }, + + report = Facts.Summarize(facts) +][report]; + +/// COMMON UNIT TESTING CODE +Fact = (_subject as text, _expected, _actual) as record => +[ expected = try _expected, + safeExpected = if expected[HasError] then "Expected : "& @ValueToText(expected[Error]) else expected[Value], + actual = try _actual, + safeActual = if actual[HasError] then "Actual : "& @ValueToText(actual[Error]) else actual[Value], + attempt = try safeExpected = safeActual, + result = if attempt[HasError] or not attempt[Value] then "Failure ⛔" else "Success ✓", + resultOp = if result = "Success ✓" then " = " else " <> ", + addendumEvalAttempt = if attempt[HasError] then @ValueToText(attempt[Error]) else "", + addendumEvalExpected = try @ValueToText(safeExpected) otherwise "...", + addendumEvalActual = try @ValueToText (safeActual) otherwise "...", + fact = + [ Result = result &" "& addendumEvalAttempt, + Notes =_subject, + Details = " ("& addendumEvalExpected & resultOp & addendumEvalActual &")" + ] +][fact]; + +Facts = (_subject as text, _predicates as list) => List.Transform(_predicates, each Fact(_subject,_{0},_{1})); + +Facts.Summarize = (_facts as list) as table => +[ Fact.CountSuccesses = (count, i) => + [ result = try i[Result], + sum = if result[HasError] or not Text.StartsWith(result[Value], "Success") then count else count + 1 + ][sum], + passed = List.Accumulate(_facts, 0, Fact.CountSuccesses), + total = List.Count(_facts), + format = if passed = total then "All #{0} Passed !!! ✓" else "#{0} Passed ☺ #{1} Failed ☹", + result = if passed = total then "Success" else "⛔", + rate = Number.IntegerDivide(100*passed, total), + header = + [ Result = result, + Notes = Text.Format(format, {passed, total-passed}), + Details = Text.Format("#{0}% success rate", {rate}) + ], + report = Table.FromRecords(List.Combine({{header},_facts})) +][report]; + +ValueToText = (value, optional depth) => + let + _canBeIdentifier = (x) => + let + keywords = {"and", "as", "each", "else", "error", "false", "if", "in", "is", "let", "meta", "not", "otherwise", "or", "section", "shared", "then", "true", "try", "type" }, + charAlpha = (c as number) => (c>= 65 and c <= 90) or (c>= 97 and c <= 122) or c=95, + charDigit = (c as number) => c>= 48 and c <= 57 + in + try + charAlpha(Character.ToNumber(Text.At(x,0))) + and + List.MatchesAll( + Text.ToList(x), + (c)=> let num = Character.ToNumber(c) in charAlpha(num) or charDigit(num) + ) + and not + List.MatchesAny( keywords, (li)=> li=x ) + otherwise + false, + + Serialize.Binary = (x) => "#binary(" & Serialize(Binary.ToList(x)) & ") ", + + Serialize.Date = (x) => "#date(" & + Text.From(Date.Year(x)) & ", " & + Text.From(Date.Month(x)) & ", " & + Text.From(Date.Day(x)) & ") ", + + Serialize.Datetime = (x) => "#datetime(" & + Text.From(Date.Year(DateTime.Date(x))) & ", " & + Text.From(Date.Month(DateTime.Date(x))) & ", " & + Text.From(Date.Day(DateTime.Date(x))) & ", " & + Text.From(Time.Hour(DateTime.Time(x))) & ", " & + Text.From(Time.Minute(DateTime.Time(x))) & ", " & + Text.From(Time.Second(DateTime.Time(x))) & ") ", + + Serialize.Datetimezone =(x) => let + dtz = DateTimeZone.ToRecord(x) + in + "#datetimezone(" & + Text.From(dtz[Year]) & ", " & + Text.From(dtz[Month]) & ", " & + Text.From(dtz[Day]) & ", " & + Text.From(dtz[Hour]) & ", " & + Text.From(dtz[Minute]) & ", " & + Text.From(dtz[Second]) & ", " & + Text.From(dtz[ZoneHours]) & ", " & + Text.From(dtz[ZoneMinutes]) & ") ", + + Serialize.Duration = (x) => let + dur = Duration.ToRecord(x) + in + "#duration(" & + Text.From(dur[Days]) & ", " & + Text.From(dur[Hours]) & ", " & + Text.From(dur[Minutes]) & ", " & + Text.From(dur[Seconds]) & ") ", + + Serialize.Function = (x) => _serialize_function_param_type( + Type.FunctionParameters(Value.Type(x)), + Type.FunctionRequiredParameters(Value.Type(x)) ) & + " as " & + _serialize_function_return_type(Value.Type(x)) & + " => (...) ", + + Serialize.List = (x) => "{" & + List.Accumulate(x, "", (seed,item) => if seed="" then Serialize(item) else seed & ", " & Serialize(item)) & + "} ", + + Serialize.Logical = (x) => Text.From(x), + + Serialize.Null = (x) => "null", + + Serialize.Number = (x) => + let Text.From = (i as number) as text => + if Number.IsNaN(i) then "#nan" else + if i=Number.PositiveInfinity then "#infinity" else + if i=Number.NegativeInfinity then "-#infinity" else + Text.From(i) + in + Text.From(x), + + Serialize.Record = (x) => "[ " & + List.Accumulate( + Record.FieldNames(x), + "", + (seed,item) => + (if seed="" then Serialize.Identifier(item) else seed & ", " & Serialize.Identifier(item)) & " = " & Serialize(Record.Field(x, item)) + ) & + " ] ", + + Serialize.Table = (x) => "#table( type " & + _serialize_table_type(Value.Type(x)) & + ", " & + Serialize(Table.ToRows(x)) & + ") ", + + Serialize.Text = (x) => """" & + _serialize_text_content(x) & + """", + + _serialize_text_content = (x) => let + escapeText = (n as number) as text => "#(#)(" & Text.PadStart(Number.ToText(n, "X", "en-US"), 4, "0") & ")" + in + List.Accumulate( + List.Transform( + Text.ToList(x), + (c) => let n=Character.ToNumber(c) in + if n = 9 then "#(#)(tab)" else + if n = 10 then "#(#)(lf)" else + if n = 13 then "#(#)(cr)" else + if n = 34 then """""" else + if n = 35 then "#(#)(#)" else + if n < 32 then escapeText(n) else + if n < 127 then Character.FromNumber(n) else + escapeText(n) + ), + "", + (s,i)=>s&i + ), + + Serialize.Identifier = (x) => + if _canBeIdentifier(x) then + x + else + "#""" & + _serialize_text_content(x) & + """", + + Serialize.Time = (x) => "#time(" & + Text.From(Time.Hour(x)) & ", " & + Text.From(Time.Minute(x)) & ", " & + Text.From(Time.Second(x)) & ") ", + + Serialize.Type = (x) => "type " & _serialize_typename(x), + + + _serialize_typename = (x, optional funtype as logical) => /* Optional parameter: Is this being used as part of a function signature? */ + let + isFunctionType = (x as type) => try if Type.FunctionReturn(x) is type then true else false otherwise false, + isTableType = (x as type) => try if Type.TableSchema(x) is table then true else false otherwise false, + isRecordType = (x as type) => try if Type.ClosedRecord(x) is type then true else false otherwise false, + isListType = (x as type) => try if Type.ListItem(x) is type then true else false otherwise false + in + + if funtype=null and isTableType(x) then _serialize_table_type(x) else + if funtype=null and isListType(x) then "{ " & @_serialize_typename( Type.ListItem(x) ) & " }" else + if funtype=null and isFunctionType(x) then "function " & _serialize_function_type(x) else + if funtype=null and isRecordType(x) then _serialize_record_type(x) else + + if x = type any then "any" else + let base = Type.NonNullable(x) in + (if Type.IsNullable(x) then "nullable " else "") & + (if base = type anynonnull then "anynonnull" else + if base = type binary then "binary" else + if base = type date then "date" else + if base = type datetime then "datetime" else + if base = type datetimezone then "datetimezone" else + if base = type duration then "duration" else + if base = type logical then "logical" else + if base = type none then "none" else + if base = type null then "null" else + if base = type number then "number" else + if base = type text then "text" else + if base = type time then "time" else + if base = type type then "type" else + + /* Abstract types: */ + if base = type function then "function" else + if base = type table then "table" else + if base = type record then "record" else + if base = type list then "list" else + + "any /*Actually unknown type*/"), + + _serialize_table_type = (x) => + let + schema = Type.TableSchema(x) + in + "table " & + (if Table.IsEmpty(schema) then "" else + "[" & List.Accumulate( + List.Transform( + Table.ToRecords(Table.Sort(schema,"Position")), + each Serialize.Identifier(_[Name]) & " = " & _[Kind]), + "", + (seed,item) => (if seed="" then item else seed & ", " & item ) + ) & "] " ), + + _serialize_record_type = (x) => + let flds = Type.RecordFields(x) + in + if Record.FieldCount(flds)=0 then "record" else + "[" & List.Accumulate( + Record.FieldNames(flds), + "", + (seed,item) => + seed & + (if seed<>"" then ", " else "") & + (Serialize.Identifier(item) & "=" & _serialize_typename(Record.Field(flds,item)[Type]) ) + ) & + (if Type.IsOpenRecord(x) then ",..." else "") & + "]", + + _serialize_function_type = (x) => _serialize_function_param_type( + Type.FunctionParameters(x), + Type.FunctionRequiredParameters(x) ) & + " as " & + _serialize_function_return_type(x), + + _serialize_function_param_type = (t,n) => + let + funsig = Table.ToRecords( + Table.TransformColumns( + Table.AddIndexColumn( Record.ToTable( t ), "isOptional", 1 ), + { "isOptional", (x)=> x>n } ) ) + in + "(" & + List.Accumulate( + funsig, + "", + (seed,item)=> + (if seed="" then "" else seed & ", ") & + (if item[isOptional] then "optional " else "") & + Serialize.Identifier(item[Name]) & " as " & _serialize_typename(item[Value], true) ) + & ")", + + _serialize_function_return_type = (x) => _serialize_typename(Type.FunctionReturn(x), true), + + Serialize = (x) as text => + if x is binary then try Serialize.Binary(x) otherwise "null /*serialize failed*/" else + if x is date then try Serialize.Date(x) otherwise "null /*serialize failed*/" else + if x is datetime then try Serialize.Datetime(x) otherwise "null /*serialize failed*/" else + if x is datetimezone then try Serialize.Datetimezone(x) otherwise "null /*serialize failed*/" else + if x is duration then try Serialize.Duration(x) otherwise "null /*serialize failed*/" else + if x is function then try Serialize.Function(x) otherwise "null /*serialize failed*/" else + if x is list then try Serialize.List(x) otherwise "null /*serialize failed*/" else + if x is logical then try Serialize.Logical(x) otherwise "null /*serialize failed*/" else + if x is null then try Serialize.Null(x) otherwise "null /*serialize failed*/" else + if x is number then try Serialize.Number(x) otherwise "null /*serialize failed*/" else + if x is record then try Serialize.Record(x) otherwise "null /*serialize failed*/" else + if x is table then try Serialize.Table(x) otherwise "null /*serialize failed*/" else + if x is text then try Serialize.Text(x) otherwise "null /*serialize failed*/" else + if x is time then try Serialize.Time(x) otherwise "null /*serialize failed*/" else + if x is type then try Serialize.Type(x) otherwise "null /*serialize failed*/" else + "[#_unable_to_serialize_#]" + in + try Serialize(value) otherwise ""; diff --git a/sql-odbc/src/PowerBIConnector/bin/Release/OdfeSqlOdbcPBIConnector.mez b/sql-odbc/src/PowerBIConnector/bin/Release/OdfeSqlOdbcPBIConnector.mez index 02d8c023ae..dc5ae9d8ab 100644 Binary files a/sql-odbc/src/PowerBIConnector/bin/Release/OdfeSqlOdbcPBIConnector.mez and b/sql-odbc/src/PowerBIConnector/bin/Release/OdfeSqlOdbcPBIConnector.mez differ