From 9dac0acbe899bf2e54206ef0652651570f180296 Mon Sep 17 00:00:00 2001 From: Rupal <56703525+rupal-bq@users.noreply.github.com> Date: Fri, 24 Jul 2020 18:20:34 -0700 Subject: [PATCH] ODBC: Adding BASIC & AWS_SIGV4 auth in M Connector (#610) * pbi connector * update names * - add mez file - add docs * nit * nit * image resize * resize image * remove extra lines * nit * update mez file * rename connector * rename mez file * - add basic & aws auth - add unit test - add errors - enable direct query * add connector * update doc * add ssl for auth * update docs for errors * - remove sqlgetinfo & sqlcapabilities values from connector * adding EncryptedConnectionString --- sql-odbc/.gitignore | 3 +- sql-odbc/docs/user/img/pbi_auth.png | Bin 12324 -> 14496 bytes sql-odbc/docs/user/img/pbi_error_conn.png | Bin 0 -> 13047 bytes .../img/pbi_error_driver_not_installed.png | Bin 0 -> 15118 bytes sql-odbc/docs/user/power_bi_support.md | 19 +- sql-odbc/src/PowerBIConnector/Diagnostics.pqm | 275 ++++ .../src/PowerBIConnector/OdbcConstants.pqm | 1253 +++++++++++++++++ .../OdfeSqlOdbcPBIConnector.mproj | 6 + .../OdfeSqlOdbcPBIConnector.pq | 197 ++- .../OdfeSqlOdbcPBIConnector.query.pq | 315 ++++- .../bin/Release/OdfeSqlOdbcPBIConnector.mez | Bin 19589 -> 33427 bytes 11 files changed, 2050 insertions(+), 18 deletions(-) create mode 100644 sql-odbc/docs/user/img/pbi_error_conn.png create mode 100644 sql-odbc/docs/user/img/pbi_error_driver_not_installed.png create mode 100644 sql-odbc/src/PowerBIConnector/Diagnostics.pqm create mode 100644 sql-odbc/src/PowerBIConnector/OdbcConstants.pqm 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 12e1af8ed6edd18d0c6f8ca760fc632a1972ce57..0795d63d95d0721d7c11ebc505c7573191a75b7d 100644 GIT binary patch literal 14496 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 XskAlMEj>}KwtprkVuyj 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b5059bc0b4af3bdaba5e969703d0da02a1a187d5 GIT binary patch literal 15118 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 -* 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 02d8c023aeae94935c8962caf72a1921977033ca..dc5ae9d8ab9ea6ac503ccbca2935edfc00ae4c4a 100644 GIT binary patch delta 15235 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 delta 1258 zcmbQ-%G5fOQ6|8fnMH(wfq{cTAmnR+n9uxL24)6^5>5sNNruUejgs|xxpPB(^KUr_ z>{b3K?`GHN;_I|{MXt+Irp~5W)3!|bCQumhhIRdxH@kMf*2tV1f6ci*=)Y6?pT@>x zDhE>ErapT+`+M5)`uhKw+Y^O2xr-GoS)we}rnIupm5?wvzF_^-M~#=+mc(@|h}--_ zZA(+2#q-DVMIz^#Uf(MaEel(JwLW|M6f3u$hTrn7T)X33w_I;}ouby-tL6O1=D_Sn zHwBGt)>z#-u6}&#<_oR)K5BRVpGrQ-WLMcyxmYQ4M^EZ%u8Bgk(;ZY7`rYU4PR>i1 zx59^MxuV%GzZRJY39ehoLV0ZhD%V~v{c$qm^!n>%2b9CzPjhVvT<`dA_myjmn=g3O zE6eOJ-|*zc$y`aN{f&H+RIkmQTc)^!=eLgE!CemoJFZumB_GI5IHHv;RM=qQ)(^ z%HFw(p}YRyOk?46Z($dIZt$;&b+P06`S0_!XB53$cKVvV=K8!{YczL=xjem?u<2#O zp@3T-P6uAS@oZN8X}u89W07`}mpo@SGMu&MIIXuTWP8XX=cc<$FGx+>es`ABlSU4f z&@$_@wx_dqB^mqun{)s1mCaB7OifF4+xu{8>C!?uG2_}U{l3)}vo}0B=iMPLyi?`Y z;<_V_X+ragUmG76zh$#zTT-dEe067WlyAngz~lC3ZTW@#iq_w}C0So`GWCt=!;KeM zW=GdvYP&Bn=~(xi?zg+kq94a{TweV^zomQ9O#PDTb!WBr)oI>hJ?%65;UwKnc9l`> zigo&L-PnA;h^LCCz2VVfU3UHPW!Uh@1l@B(1&Ry(0$&b&Y>h8%& zVb21muDAXj7J1-J>9>=Lq4V}wDu}-PvSfwP=Rfj@j8YQ#b@MfgcTCJ6#&PTO%wWb$ z`x7jDK#>5=TBnA&Yzz!D*d{-;ke$rm$hLWs%W^ibA{*}+9AL)v;N6^H)#An{=j`NR zVA#R~R{a5}`f%)F9x z#K5o-h-Hx!F))}jgB9vE2!Rz=Y981z9hl=M0WqpV4K}dC_ttEa>l?rhn%vhQ&Gd^C zEPSLv2;|1eH-SP&`M^T_jSwSs|7xvE;9y{g24Ykr=ZXRqYJ>IVH%c>E$N{-hlR{W0 zuWp1`unQ>qO&x672dHU6+m0uz^D!_e0Wqp+uS~!St($~EfiyXwNt(&Wd2()(1Roaz O7sG9!&!>2Tybb`&XBDUb