namespace SqlDynamite.Common

open System
open System.Data
open System.Data.Common
open System.IO
open System.Reflection
open FirebirdSql.Data.FirebirdClient
open System.Data.SQLite
open MySql.Data.MySqlClient
open Npgsql
open Oracle.ManagedDataAccess.Client
open MongoDB.Driver
open MongoDB.Bson
open Cassandra.Data
open StackExchange.Redis
open Neo4j.Driver
open Microsoft.AnalysisServices.AdomdClient
open InterBaseSql.Data.InterBaseClient
open Sybase.Data.AseClient
open Vertica.Data.VerticaClient
open Ingres.Client
open Advantage.Data.Provider
open Sap.Data.Hana
open Sap.Data.SQLAnywhere
open Sap.Data.UltraLite

type MetadataFinderHelper() =

    static let DirSeparator = sprintf "%c" Path.DirectorySeparatorChar
    static let currentDirectory = Directory.GetCurrentDirectory() + DirSeparator
    static let informix_dll = currentDirectory + "INFORMIX" + DirSeparator + "bin" + DirSeparator + "netf40" + DirSeparator + "IBM.Data.Informix.dll"
    static let db2_dll = currentDirectory + "IBM" + DirSeparator + "bin" + DirSeparator + "netf40" + DirSeparator + "IBM.Data.DB2.dll"
    
    static let _providers = [("Informix","IBM.Data.Informix");("DB2","IBM.Data.DB2")]

    static member SystemDbs = dict [("MSSQL",["master";"model";"tempdb";"msdb";"rdsadmin"]);
                               ("SQL Analysis Services",[]);
                               ("SQL Azure",["master"]);
                               ("SAP ASE",["master";"model";"tempdb";"sybsystemdb";"sybsystemprocs"]);
                               ("MySQL",["mysql";"sys";"information_schema";"performance_schema";"rdsadmin"]);
                               ("PostgreSQL",["postgres";"rdsadmin";"template0";"template1";"azure_maintenance";"azure_sys"]);
                               ("DB2",["rdsadmin"]);
                               ("Informix",["sysadmin";"sysmaster";"sysuser";"sysutils"]);
                               ("Redis",[]);
                               ("MongoDb",["admin";"config";"local"]);
                               ("Cassandra",["system";"system_auth";"system_distributed";"system_schema";"system_traces"])]

    static member ProvidersList : (string * string) list =
        let platform = Environment.OSVersion.Platform
        let gacFolders = ["gac";"GAC_MSIL";"GAC_32";"GAC_64"]
        let paths =
            if platform = PlatformID.Win32NT then [@"%systemroot%\assembly"; @"%systemroot%\Microsoft.NET\assembly"]
            elif platform = PlatformID.Unix && Environment.OSVersion.Version.Major >= 8 || platform = PlatformID.MacOSX then ["/Library/Frameworks/Mono.framework/Libraries/mono"]
            elif platform = PlatformID.Unix && Environment.OSVersion.Version.Major <= 7 then ["/usr/lib/mono"]
            else []
        let rec loop gacFolder n acc =
            if n > 0 then
                let pathStr = Path.Combine(Environment.ExpandEnvironmentVariables(paths.Item(n - 1)), gacFolder)
                if Directory.Exists(pathStr) then
                    loop gacFolder (n - 1) acc @ MetadataFinderHelper.CheckAssemblies(pathStr)
                else
                    loop gacFolder (n - 1) acc
            else
                acc
        let rec extLoop n acc =
            if n > 0 then
                extLoop (n - 1) acc @ loop (gacFolders.Item(n - 1)) paths.Length []
            else
                acc
        extLoop gacFolders.Length []
    
    static member CheckAssemblies(path:string) : (string * string) list =
        let assemblyFolders = Directory.GetDirectories(path)
        let rec loop (str:string) n acc =
            if n > 0 then
                if str.ToLower().StartsWith((snd (_providers.Item(n - 1))).ToLower()) then
                    let directories = Directory.GetDirectories(path + DirSeparator + str)
                    if directories.Length > 0 then
                        let info = directories.[0].Split([|"__"|], StringSplitOptions.None)
                        if info.Length > 1  && not (info.[0].Contains(".EF6") || info.[0].Contains(".Entity") || info.[0].Contains(".ASP")) then
                            let parts = info.[0].Replace(path + DirSeparator + str + DirSeparator, "").Split('_')
                            let version = if parts.Length = 1 then parts.[0] else parts.[1]
                            let publicKeyToken = info.[1]
                            loop str (n - 1) ((fst (_providers.Item(n - 1)), str + ", Version=" + version + ", Culture=neutral, PublicKeyToken=" + publicKeyToken) :: acc)
                        else
                            loop str (n - 1) acc
                    else
                        loop str (n - 1) acc
                else
                    loop str (n - 1) acc
            else
                acc
        let rec extLoop n acc =
            if n > 0 then
                let str = assemblyFolders.[n - 1].Replace(path + DirSeparator, "")
                extLoop (n - 1) acc @ loop str _providers.Length []
            else
                acc
        extLoop assemblyFolders.Length []
    
    static member CreateTypeInstance(serverType:string, assemblyFileName:string, typeName:string, [<ParamArray>] args : obj array) : obj =
        let gacAssembly =
            if List.exists (fun item -> fst item = serverType) MetadataFinderHelper.ProvidersList
                then MetadataFinderHelper.ProvidersList |> List.find (fun item -> fst item = serverType) |> snd
                else null
        let assembly = 
            if gacAssembly = null then 
                if assemblyFileName <> null then
                    Assembly.LoadFile(assemblyFileName) 
                else
                    raise (ArgumentException(String.Format("Could not find data provider for {0}", serverType)))
            else 
                Assembly.Load(gacAssembly)
        let typ = assembly.GetType(typeName)
        Activator.CreateInstance(typ, args)
    
    static member CreateConnection(finder, connectionString:string) : DbConnection =
        match finder.GetType().Name with
        | "FirebirdMetadataFinder" -> new FbConnection(connectionString)
        | "InterbaseMetadataFinder" -> new IBConnection(connectionString)
        | "SqliteMetadataFinder" -> new SQLiteConnection(connectionString)
        | "MySqlMetadataFinder" -> new MySqlConnection(connectionString)
        | "PostgreSqlMetadataFinder" -> new NpgsqlConnection(connectionString)
        | "OracleMetadataFinder" -> new OracleConnection(connectionString)
        | "MongoDbMetadataFinder" -> new MongoConnection(ConnectionString = connectionString)
        | "RedisMetadataFinder" -> new RedisConnection(ConnectionString = connectionString)
        | "Neo4jMetadataFinder" -> new Neo4jConnection(ConnectionString = connectionString)
        | "CassandraMetadataFinder" -> new CqlConnection(connectionString)
        | "IngresMetadataFinder" -> new IngresConnection(connectionString)
        | "InformixMetadataFinder" -> MetadataFinderHelper.CreateTypeInstance("Informix", informix_dll, "IBM.Data.Informix.IfxConnection", connectionString) :?> DbConnection
        | "Db2MetadataFinder" -> MetadataFinderHelper.CreateTypeInstance("DB2", db2_dll, "IBM.Data.DB2.DB2Connection", connectionString) :?> DbConnection
        //| "InformixMetadataFinder" -> new IfxConnection(connectionString)
        //| "Db2MetadataFinder" -> new DB2Connection(connectionString)
        | "VerticaMetadataFinder" -> new VerticaConnection(connectionString)
        | "SybaseAnywhereMetadataFinder" -> new SAConnection(connectionString)
        | "SybaseMetadataFinder" -> new AseConnection(connectionString)
        | "SapHanaMetadataFinder" -> new HanaConnection(connectionString)
        | "UltraliteMetadataFinder" -> new ULConnection(connectionString)
        | "SybaseAdvantageMetadataFinder" -> 
                                    new WrapperConnection(new AdsConnection(connectionString))
        | "SsasMetadataFinder" -> 
                                    new WrapperConnection(new AdomdConnection(connectionString))
        | _ -> null

    static member CreateDataSet(connection:DbConnection, query:string) : DataSet =
        match connection with
        | null ->
            null
        | :? FbConnection ->
            let command = new FbCommand(query, connection :?> FbConnection)
            command.CommandTimeout <- 3600
            let adapter = new FbDataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? IBConnection ->
            let command = new IBCommand(query, connection :?> IBConnection)
            command.CommandTimeout <- 3600
            let adapter = new IBDataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? SQLiteConnection ->
            let command = new SQLiteCommand(query, connection :?> SQLiteConnection)
            command.CommandTimeout <- 3600
            let adapter = new SQLiteDataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? MySqlConnection ->
            let command = new MySqlCommand(query, connection :?> MySqlConnection)
            command.CommandTimeout <- 3600
            let adapter = new MySqlDataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? NpgsqlConnection ->
            let command = new NpgsqlCommand(query, connection :?> NpgsqlConnection)
            command.CommandTimeout <- 3600
            let adapter = new NpgsqlDataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? OracleConnection ->
            let command = new OracleCommand(query, connection :?> OracleConnection)
            command.CommandTimeout <- 3600
            let adapter = new OracleDataAdapter(command)
            let dataSet = new DataSet()
            let _ = adapter.Fill(dataSet)
            dataSet
        | :? IngresConnection ->
            let command = new IngresCommand(query, connection :?> IngresConnection)
            command.CommandTimeout <- 3600
            let adapter = new IngresDataAdapter(command)
            let dataSet = new DataSet()
            let _ = adapter.Fill(dataSet)
            dataSet
        | :? VerticaConnection ->
            let command = new VerticaCommand(query, connection :?> VerticaConnection)
            command.CommandTimeout <- 3600
            let adapter = new VerticaDataAdapter(command)
            let dataSet = new DataSet()
            let _ = adapter.Fill(dataSet)
            dataSet
        | :? AseConnection ->
            let command = new AseCommand(query, connection :?> AseConnection)
            command.CommandTimeout <- 3600
            let adapter = new AseDataAdapter(command)
            let dataSet = new DataSet()
            let _ = adapter.Fill(dataSet)
            dataSet
        | :? SAConnection ->
            let command = new SACommand(query, connection :?> SAConnection)
            command.CommandTimeout <- 3600
            let adapter = new SADataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? ULConnection ->
            let command = new ULCommand(query, connection :?> ULConnection)
            let adapter = new ULDataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? HanaConnection ->
            let command = new HanaCommand(query, connection :?> HanaConnection)
            let adapter = new HanaDataAdapter(command)
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? WrapperConnection ->
            let wrapperConnection = connection :?> WrapperConnection
            match wrapperConnection.InnerConnection with
            | :? AdsConnection ->
                let adsCommand = new AdsCommand(query, wrapperConnection.InnerConnection :?> AdsConnection)
                let command = new WrapperCommand(adsCommand)
                command.CommandTimeout <- 3600
                let adapter = new AdsDataAdapter(command.InnerCommand :?> AdsCommand)
                let dataSet = new DataSet()
                ignore(adapter.Fill(dataSet))
                dataSet
            | :? AdomdConnection ->
                let adomdCommand = new AdomdCommand(query, wrapperConnection.InnerConnection :?> AdomdConnection)
                let command = new WrapperCommand(adomdCommand)
                command.CommandTimeout <- 3600
                let adapter = new AdomdDataAdapter(command.InnerCommand :?> AdomdCommand)
                let dataSet = new DataSet()
                ignore(adapter.Fill(dataSet))
                dataSet
            | _ -> null
        (*| :? DB2Connection ->
            //let command = new DB2Command(query, connection :?> DB2Connection)
            let command = MetadataFinderHelper.CreateTypeInstance("DB2", db2_dll, "IBM.Data.DB2.DB2Command", query, connection) :?> DbCommand
            command.CommandTimeout <- 3600
            //let adapter = new DB2DataAdapter(command)
            let adapter = MetadataFinderHelper.CreateTypeInstance("DB2", db2_dll, "IBM.Data.DB2.DB2DataAdapter", command) :?> DbDataAdapter
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet
        | :? IfxConnection ->
            //let command = new IfxCommand(query, connection :?> IfxConnection)
            let command = MetadataFinderHelper.CreateTypeInstance("Informix", informix_dll, "IBM.Data.Informix.IfxCommand", query, connection) :?> DbCommand
            command.CommandTimeout <- 3600
            //let adapter = new IfxDataAdapter(command)
            let adapter = MetadataFinderHelper.CreateTypeInstance("Informix", informix_dll, "IBM.Data.Informix.IfxDataAdapter", command) :?> DbDataAdapter
            let dataSet = new DataSet()
            ignore(adapter.Fill(dataSet))
            dataSet*)
        | :? MongoConnection ->
            let dataSet = new DataSet()
            let dataTable = new DataTable()
            ignore(dataTable.Columns.Add("Name"))
            ignore(dataTable.Columns.Add("ObjectType"))
            
            let mongoUrl = MongoUrl(connection.ConnectionString)
            let mongoUrlDb = mongoUrl.DatabaseName
            let connStr = if connection.ConnectionString.Contains("mongodb.net") && mongoUrlDb <> null then connection.ConnectionString.Replace("/" + mongoUrlDb.ToString() + "?ssl=true", "?ssl=true") else connection.ConnectionString
            let client = MongoClient(connStr)
            let databaseNames = client.ListDatabaseNames()
            let databasesExist = databaseNames.MoveNext()
            if not databasesExist then raise (Exception("Databases do not exist in the selected cluster"))
            let databaseNameList = databaseNames.Current :?> System.Collections.Generic.List<string>
            let dbname = if mongoUrlDb = null then databaseNameList.[0] else mongoUrlDb.ToString()
            let database = client.GetDatabase(dbname)
            
            let collections = database.ListCollectionNames()
            let _ = collections.MoveNext()
            let collectionNameList = collections.Current
            for col in collectionNameList do
                let colName = col.ToString()
                if colName.Contains(query) then
                    let dataRow = dataTable.NewRow()
                    dataRow.Item "Name" <- dbname + " : " + colName
                    dataRow.Item "ObjectType" <- "TABLE"
                    dataTable.Rows.Add(dataRow)

                try
                    let collection = database.GetCollection(colName)
                    let indexes = collection.Indexes
                    let indexNames = indexes.List()
                    let _ = indexNames.MoveNext()
                    let indexNameList = indexNames.Current
                    for index in indexNameList do
                        let name = index.GetValue("name").ToString()
                        if name <> "_id_" && name.Contains(query) then
                            let dataRow = dataTable.NewRow()
                            dataRow.Item "Name" <- dbname + " : " + colName + " : " + name
                            dataRow.Item "ObjectType" <- "INDEX"
                            dataTable.Rows.Add(dataRow)
                with
                    exc -> 
                        if exc.InnerException <> null && exc.InnerException :? MongoCommandException then
                            dataTable.Rows.Item(dataTable.Rows.Count - 1).Item "ObjectType" <- "VIEW"
                        else
                            ()
            
            try
                let systemcollection = database.GetCollection("system.js")
                let bsonDocument = BsonDocument()
                let bsonDocumentFilterDefinition = BsonDocumentFilterDefinition<BsonDocument>(bsonDocument)
                let functions = systemcollection.Find(bsonDocumentFilterDefinition)
                let cursor = functions.ToCursor()
                let _ = cursor.MoveNext()
                let funcList = cursor.Current
                for fn in funcList do
                    let fnName = fn.GetValue("_id").ToString()
                    if fnName.Contains(query) then
                        let dataRow = dataTable.NewRow()
                        dataRow.Item "Name" <- dbname + " : " + fnName
                        dataRow.Item "ObjectType" <- "FUNCTION"
                        dataTable.Rows.Add(dataRow)
            with
                _ -> ()
            dataSet.Tables.Add(dataTable)
            dataSet
        | :? CqlConnection ->
            let getData (ds:DataSet, script) =
                let command = new CqlCommand()
                command.CommandText <- script
                command.Connection <- connection
                let adapter = new CqlDataAdapter()
                adapter.SelectCommand <- command
                ignore(adapter.Fill(ds))
            let dataSet = new DataSet()
            if query.Split(' ').Length > 1 then
                getData(dataSet, query)
            else
                let keyspace = if connection.ConnectionString.ToLower().Contains("default keyspace=") then connection.ConnectionString.ToLower().Replace("default keyspace=", "").Split(';').[0] else null
                getData(dataSet, "select AGGREGATE_NAME, KEYSPACE_NAME from SYSTEM_SCHEMA.AGGREGATES")
                let aggregates = [for row in dataSet.Tables.[0].Rows do yield (row.Item("AGGREGATE_NAME").ToString(), row.Item("KEYSPACE_NAME").ToString())]
                dataSet.Clear()
                getData(dataSet, "select TRIGGER_NAME, KEYSPACE_NAME from SYSTEM_SCHEMA.TRIGGERS")
                let triggers = [for row in dataSet.Tables.[0].Rows do yield (row.Item("TRIGGER_NAME").ToString(), row.Item("KEYSPACE_NAME").ToString())]
                dataSet.Clear()
                getData(dataSet, "select VIEW_NAME, KEYSPACE_NAME from SYSTEM_SCHEMA.VIEWS")
                let views = [for row in dataSet.Tables.[0].Rows do yield (row.Item("VIEW_NAME").ToString(), row.Item("KEYSPACE_NAME").ToString())]
                dataSet.Clear()
                getData(dataSet, "select TABLE_NAME, KEYSPACE_NAME from SYSTEM_SCHEMA.TABLES")
                let tables = [for row in dataSet.Tables.[0].Rows do yield (row.Item("TABLE_NAME").ToString(), row.Item("KEYSPACE_NAME").ToString())]
                dataSet.Clear()
                getData(dataSet, "select INDEX_NAME, KEYSPACE_NAME from SYSTEM_SCHEMA.INDEXES")
                let indexes = [for row in dataSet.Tables.[0].Rows do yield (row.Item("INDEX_NAME").ToString(), row.Item("KEYSPACE_NAME").ToString())]
                dataSet.Clear()
                getData(dataSet, "select TYPE_NAME, KEYSPACE_NAME from SYSTEM_SCHEMA.TYPES")
                let types = [for row in dataSet.Tables.[0].Rows do yield (row.Item("TYPE_NAME").ToString(), row.Item("KEYSPACE_NAME").ToString())]
                dataSet.Clear()
                getData(dataSet, "select TABLE_NAME, COLUMN_NAME, KEYSPACE_NAME from SYSTEM_SCHEMA.COLUMNS")
                let columns = [for row in dataSet.Tables.[0].Rows do yield [row.Item("TABLE_NAME").ToString(); row.Item("COLUMN_NAME").ToString(); row.Item("KEYSPACE_NAME").ToString()]]
                dataSet.Clear()
                getData(dataSet, "select FUNCTION_NAME, BODY, KEYSPACE_NAME from SYSTEM_SCHEMA.FUNCTIONS")
                let functions = [for row in dataSet.Tables.[0].Rows do yield [row.Item("FUNCTION_NAME").ToString(); row.Item("BODY").ToString(); row.Item("KEYSPACE_NAME").ToString()]]
                dataSet.Clear()
                let list = System.Collections.Generic.List<string * string>()
                for item in aggregates do if (fst item).Contains(query) && (String.IsNullOrWhiteSpace(keyspace) || snd item = keyspace) then list.Add((fst item, "AGGREGATE"))
                for item in triggers do if (fst item).Contains(query) && (String.IsNullOrWhiteSpace(keyspace) || snd item = keyspace) then list.Add((fst item, "TRIGGER"))
                for item in views do if (fst item).Contains(query) && (String.IsNullOrWhiteSpace(keyspace) || snd item = keyspace) then list.Add((fst item, "VIEW"))
                for item in tables do if (fst item).Contains(query) && (String.IsNullOrWhiteSpace(keyspace) || snd item = keyspace) then list.Add((fst item, "TABLE"))
                for item in indexes do if (fst item).Contains(query) && (String.IsNullOrWhiteSpace(keyspace) || snd item = keyspace) then list.Add((fst item, "INDEX"))
                for item in types do if (fst item).Contains(query) && (String.IsNullOrWhiteSpace(keyspace) || snd item = keyspace) then list.Add((fst item, "TYPE"))
                for item in functions do if (item.Item(0).Contains(query) || item.Item(1).Contains(query)) && (String.IsNullOrWhiteSpace(keyspace) || item.Item(2) = keyspace) then list.Add((item.Item(0), "FUNCTION"))
                for item in columns do
                    if item.Item(1).Contains(query) && (String.IsNullOrWhiteSpace(keyspace) || item.Item(2) = keyspace) then
                        let isTable = List.exists (fun elem -> fst elem = item.Item(0)) tables
                        if isTable then
                            list.Add((item.Item(0), "TABLE"))
                        else
                            list.Add((item.Item(0), "VIEW"))
                    else
                        ()
                let dataTable = new DataTable()
                ignore(dataTable.Columns.Add("Name"))
                ignore(dataTable.Columns.Add("ObjectType"))
                for item in list do
                    let dataRow = dataTable.NewRow()
                    dataRow.Item "Name" <- fst item
                    dataRow.Item "ObjectType" <- snd item
                    dataTable.Rows.Add(dataRow)
                dataSet.Tables.RemoveAt(0)
                dataSet.Tables.Add(dataTable)
            dataSet
        | :? RedisConnection ->
            let dataSet = new DataSet()
            let dataTable = new DataTable()
            ignore(dataTable.Columns.Add("Name"))
            ignore(dataTable.Columns.Add("ObjectType"))
            let parts = connection.ConnectionString.Split(';')
            let cs = parts.[0].Split(':')
            let db = if String.IsNullOrWhiteSpace(parts.[1]) then 0 else Int32.Parse(parts.[1].Replace("db", ""))
            let port = if cs.Length = 2 then Int32.Parse(cs.[1]) else 6379
            let password = if parts.Length = 3 then parts.[2].Replace("Password=", "") else ""
            let co = new ConfigurationOptions()
            co.EndPoints.Add(cs.[0], port)
            co.DefaultDatabase <- db
            if not (String.IsNullOrWhiteSpace(password)) then co.Password <- password
            let cm = ConnectionMultiplexer.Connect(co)
            let server = cm.GetServer(cs.[0], port)
            let database = cm.GetDatabase(db)
            let keys = server.Keys(db)
            for key in keys do
                if key.ToString().Contains(query) then
                    let dataRow = dataTable.NewRow()
                    dataRow.Item "Name" <- key
                    dataRow.Item "ObjectType" <- database.KeyType(key).ToString().ToUpper()
                    dataTable.Rows.Add(dataRow)
            dataSet.Tables.Add(dataTable)
            dataSet
        | :? Neo4jConnection ->
            let dataSet = new DataSet()
            let dataTable = new DataTable()
            ignore(dataTable.Columns.Add("Name"))
            ignore(dataTable.Columns.Add("ObjectType"))

            let parts = connection.ConnectionString.Split(';')
            let url = parts.[0]
            let username = parts.[1]
            let password = parts.[2]
            let authToken = if (String.IsNullOrWhiteSpace(password) || String.IsNullOrWhiteSpace(username)) then AuthTokens.None else AuthTokens.Basic(username, password)

            let driver = GraphDatabase.Driver(url, authToken)
            let indexes = Neo4jConnection.GetData(driver, String.Format("SHOW INDEXES
            YIELD name, createStatement
            WHERE toLower(name) CONTAINS toLower('{0}') OR toLower(createStatement) CONTAINS toLower('{0}')
            RETURN name", query))
            for idx in indexes do
                ignore(dataTable.Rows.Add(idx.Item("name"), "INDEX"))
            let labels = Neo4jConnection.GetData(driver, String.Format("call db.labels()
            YIELD label
            WITH label
            WHERE toLower(label) CONTAINS toLower('{0}')
            RETURN label", query))
            for label in labels do
                ignore(dataTable.Rows.Add(label.Item("label"), "LABEL"))
            let functions = Neo4jConnection.GetData(driver, String.Format("SHOW FUNCTIONS
            YIELD name
            WHERE toLower(name) CONTAINS toLower('{0}')
            RETURN name", query))
            for fn in functions do
                ignore(dataTable.Rows.Add(fn.Item("name"), "FUNCTION"))
            let procedures = Neo4jConnection.GetData(driver, String.Format("SHOW PROCEDURES
            YIELD name
            WHERE toLower(name) CONTAINS toLower('{0}')
            RETURN name", query))
            for procedure in procedures do
                ignore(dataTable.Rows.Add(procedure.Item("name"), "PROCEDURE"))

            dataSet.Tables.Add(dataTable)
            dataSet
        | _ ->
            match connection.GetType().FullName with
            | "IBM.Data.Informix.IfxConnection" ->
                let command = MetadataFinderHelper.CreateTypeInstance("Informix", informix_dll, "IBM.Data.Informix.IfxCommand", query, connection) :?> DbCommand
                command.CommandTimeout <- 3600
                let adapter = MetadataFinderHelper.CreateTypeInstance("Informix", informix_dll, "IBM.Data.Informix.IfxDataAdapter", command) :?> DbDataAdapter
                let dataSet = new DataSet()
                ignore(adapter.Fill(dataSet))
                dataSet
            | "IBM.Data.DB2.DB2Connection" ->
                let command = MetadataFinderHelper.CreateTypeInstance("DB2", db2_dll, "IBM.Data.DB2.DB2Command", query, connection) :?> DbCommand
                command.CommandTimeout <- 3600
                let adapter = MetadataFinderHelper.CreateTypeInstance("DB2", db2_dll, "IBM.Data.DB2.DB2DataAdapter", command) :?> DbDataAdapter
                let dataSet = new DataSet()
                ignore(adapter.Fill(dataSet))
                dataSet
            | _ -> null

    static member UseDatabase(connection:DbConnection, database:string) =
        match connection with
        | :? MySqlConnection ->
                let cmd = new MySqlCommand("use `" + database + "`", connection :?> MySqlConnection)
                ignore(cmd.ExecuteNonQuery())
        | :? AseConnection ->
                let cmd = new AseCommand("use " + database, connection :?> AseConnection)
                ignore(cmd.ExecuteNonQuery())
        | _ -> ()
