[
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n  \"image\": \"mcr.microsoft.com/devcontainers/universal:2\",\n  \"features\": {\n    \"ghcr.io/devcontainers/features/dotnet:1\": {}\n  },\n  \"customizations\": {\n    \"vscode\": {\n      \"extensions\": [\n        \"ms-dotnettools.csharp\",\n        \"eamodio.gitlens\",\n        \"formulahendry.dotnet-test-explorer\",\n        \"mtxr.sqltools-driver-pg\"\n      ]\n    }\n  },\n  \"containerEnv\": {\n    \"DOTNET_CLI_HOME\": \"/tmp/DOTNET_CLI_HOME\"\n  },\n  \"postCreateCommand\": \"./.devcontainer/init.sh\",\n  \"postStartCommand\": \"sudo service postgresql start\"\n}\n"
  },
  {
    "path": ".devcontainer/init.sh",
    "content": "sudo apt update\nsudo apt install postgresql postgresql-contrib -y\nsudo cp ./.devcontainer/pg_hba.conf /etc/postgresql/12/main/pg_hba.conf\nsudo chown postgres:postgres /etc/postgresql/12/main/pg_hba.conf\nsudo service postgresql start\n\nsudo psql -U postgres -c \"ALTER USER postgres PASSWORD 'password';\"\n\ndotnet restore"
  },
  {
    "path": ".devcontainer/pg_hba.conf",
    "content": "# PostgreSQL Client Authentication Configuration File\n# ===================================================\n#\n# Refer to the \"Client Authentication\" section in the PostgreSQL\n# documentation for a complete description of this file.  A short\n# synopsis follows.\n#\n# This file controls: which hosts are allowed to connect, how clients\n# are authenticated, which PostgreSQL user names they can use, which\n# databases they can access.  Records take one of these forms:\n#\n# local         DATABASE  USER  METHOD  [OPTIONS]\n# host          DATABASE  USER  ADDRESS  METHOD  [OPTIONS]\n# hostssl       DATABASE  USER  ADDRESS  METHOD  [OPTIONS]\n# hostnossl     DATABASE  USER  ADDRESS  METHOD  [OPTIONS]\n# hostgssenc    DATABASE  USER  ADDRESS  METHOD  [OPTIONS]\n# hostnogssenc  DATABASE  USER  ADDRESS  METHOD  [OPTIONS]\n#\n# (The uppercase items must be replaced by actual values.)\n#\n# The first field is the connection type: \"local\" is a Unix-domain\n# socket, \"host\" is either a plain or SSL-encrypted TCP/IP socket,\n# \"hostssl\" is an SSL-encrypted TCP/IP socket, and \"hostnossl\" is a\n# non-SSL TCP/IP socket.  Similarly, \"hostgssenc\" uses a\n# GSSAPI-encrypted TCP/IP socket, while \"hostnogssenc\" uses a\n# non-GSSAPI socket.\n#\n# DATABASE can be \"all\", \"sameuser\", \"samerole\", \"replication\", a\n# database name, or a comma-separated list thereof. The \"all\"\n# keyword does not match \"replication\". Access to replication\n# must be enabled in a separate record (see example below).\n#\n# USER can be \"all\", a user name, a group name prefixed with \"+\", or a\n# comma-separated list thereof.  In both the DATABASE and USER fields\n# you can also write a file name prefixed with \"@\" to include names\n# from a separate file.\n#\n# ADDRESS specifies the set of hosts the record matches.  It can be a\n# host name, or it is made up of an IP address and a CIDR mask that is\n# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that\n# specifies the number of significant bits in the mask.  A host name\n# that starts with a dot (.) matches a suffix of the actual host name.\n# Alternatively, you can write an IP address and netmask in separate\n# columns to specify the set of hosts.  Instead of a CIDR-address, you\n# can write \"samehost\" to match any of the server's own IP addresses,\n# or \"samenet\" to match any address in any subnet that the server is\n# directly connected to.\n#\n# METHOD can be \"trust\", \"reject\", \"md5\", \"password\", \"scram-sha-256\",\n# \"gss\", \"sspi\", \"ident\", \"peer\", \"pam\", \"ldap\", \"radius\" or \"cert\".\n# Note that \"password\" sends passwords in clear text; \"md5\" or\n# \"scram-sha-256\" are preferred since they send encrypted passwords.\n#\n# OPTIONS are a set of options for the authentication in the format\n# NAME=VALUE.  The available options depend on the different\n# authentication methods -- refer to the \"Client Authentication\"\n# section in the documentation for a list of which options are\n# available for which authentication methods.\n#\n# Database and user names containing spaces, commas, quotes and other\n# special characters must be quoted.  Quoting one of the keywords\n# \"all\", \"sameuser\", \"samerole\" or \"replication\" makes the name lose\n# its special character, and just match a database or username with\n# that name.\n#\n# This file is read on server startup and when the server receives a\n# SIGHUP signal.  If you edit the file on a running system, you have to\n# SIGHUP the server for the changes to take effect, run \"pg_ctl reload\",\n# or execute \"SELECT pg_reload_conf()\".\n#\n# Put your actual configuration here\n# ----------------------------------\n#\n# If you want to allow non-local connections, you need to add more\n# \"host\" records.  In that case you will also need to make PostgreSQL\n# listen on a non-local interface via the listen_addresses\n# configuration parameter, or via the -i or -h command line switches.\n\n\n\n\n# DO NOT DISABLE!\n# If you change this first entry you will need to make sure that the\n# database superuser can access the database using some other method.\n# Noninteractive access to all databases is required during automatic\n# maintenance (custom daily cronjobs, replication, and similar tasks).\n#\n# Database administrative login by Unix domain socket\nlocal   all             postgres                                trust\nlocal   all             postgres                                md5\n\n# TYPE  DATABASE        USER            ADDRESS                 METHOD\n\n# \"local\" is for Unix domain socket connections only\nlocal   all             all                                     peer\n# IPv4 local connections:\nhost    all             all             127.0.0.1/32            md5\n# IPv6 local connections:\nhost    all             all             ::1/128                 md5\n# Allow replication connections from localhost, by a user with the\n# replication privilege.\nlocal   replication     all                                     peer\nhost    replication     all             127.0.0.1/32            md5\nhost    replication     all             ::1/128                 md5"
  },
  {
    "path": ".editorconfig",
    "content": "\n[*]\ncharset = utf-8-bom\nend_of_line = lf\ntrim_trailing_whitespace = false\nindent_style = space\nindent_size = 2\n\n# Microsoft .NET properties\n# GLOBAL ERROR LEVEL\ndotnet_analyzer_diagnostic.severity = warning\n# A property with [Parameter] should be public\ndotnet_diagnostic.BL0004.severity = error\n# Method does not use this and can be marked static\ndotnet_diagnostic.CA1822.severity = suggestion\n# An IDisposable implementation should call GC.SuppressFinalize\ndotnet_diagnostic.CA1816.severity = none\n# The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings.\ndotnet_diagnostic.CA1310.severity = suggestion\n# The behavior of 'string.ToLower(string)' could vary based on the current user's locale settings.\ndotnet_diagnostic.CA1304.severity = suggestion\n# Identifiers should not have incorrect suffix (Do not use reserved words in type names)\ndotnet_diagnostic.CA1711.severity = suggestion\n# Identifiers should not match keywords\ndotnet_diagnostic.CA1716.severity = suggestion\n# Identifiers should not contain underscores\ndotnet_diagnostic.CA1707.severity = suggestion\n\n[*]\ncsharp_indent_braces = false\ncsharp_indent_switch_labels = true\ncsharp_new_line_before_catch = true\ncsharp_new_line_before_else = true\ncsharp_new_line_before_finally = true\ncsharp_new_line_before_members_in_object_initializers = false\ncsharp_new_line_before_open_brace = control_blocks,events,indexers,local_functions,methods,properties,types\ncsharp_new_line_between_query_expression_clauses = true\ncsharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:warning\ncsharp_preserve_single_line_blocks = true\ncsharp_space_after_cast = false\ncsharp_space_after_colon_in_inheritance_clause = true\ncsharp_space_after_comma = true\ncsharp_space_after_dot = false\ncsharp_space_after_keywords_in_control_flow_statements = true\ncsharp_space_after_semicolon_in_for_statement = true\ncsharp_space_around_binary_operators = before_and_after\ncsharp_space_before_colon_in_inheritance_clause = true\ncsharp_space_before_comma = false\ncsharp_space_before_dot = false\ncsharp_space_before_open_square_brackets = false\ncsharp_space_before_semicolon_in_for_statement = false\ncsharp_space_between_empty_square_brackets = false\ncsharp_space_between_method_call_empty_parameter_list_parentheses = false\ncsharp_space_between_method_call_name_and_opening_parenthesis = false\ncsharp_space_between_method_call_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_empty_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_name_and_open_parenthesis = false\ncsharp_space_between_method_declaration_parameter_list_parentheses = false\ncsharp_space_between_parentheses = false\ncsharp_space_between_square_brackets = false\ncsharp_style_unused_value_expression_statement_preference = discard_variable:none\ncsharp_style_var_elsewhere = false:warning\ncsharp_style_var_for_built_in_types = false:warning\ncsharp_style_var_when_type_is_apparent = false:warning\ncsharp_using_directive_placement = outside_namespace:single_indent\ndotnet_naming_rule.constants_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.constants_rule.severity = warning\ndotnet_naming_rule.constants_rule.style = upper_camel_case_style\ndotnet_naming_rule.constants_rule.symbols = constants_symbols\ndotnet_naming_rule.event_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.event_rule.severity = warning\ndotnet_naming_rule.event_rule.style = upper_camel_case_style\ndotnet_naming_rule.event_rule.symbols = event_symbols\ndotnet_naming_rule.interfaces_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.interfaces_rule.severity = warning\ndotnet_naming_rule.interfaces_rule.style = i_upper_camel_case_style\ndotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols\ndotnet_naming_rule.locals_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.locals_rule.severity = warning\ndotnet_naming_rule.locals_rule.style = lower_camel_case_style_1\ndotnet_naming_rule.locals_rule.symbols = locals_symbols\ndotnet_naming_rule.local_constants_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.local_constants_rule.severity = warning\ndotnet_naming_rule.local_constants_rule.style = lower_camel_case_style_1\ndotnet_naming_rule.local_constants_rule.symbols = local_constants_symbols\ndotnet_naming_rule.local_constants_rule_1.import_to_resharper = True\ndotnet_naming_rule.local_constants_rule_1.resharper_description = LocalConstants\ndotnet_naming_rule.local_constants_rule_1.resharper_guid = 02852745-d182-4e0a-8332-8b22091b5e66\ndotnet_naming_rule.local_constants_rule_1.severity = warning\ndotnet_naming_rule.local_constants_rule_1.style = lower_camel_case_style_1\ndotnet_naming_rule.local_constants_rule_1.symbols = local_constants_symbols_1\ndotnet_naming_rule.local_functions_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.local_functions_rule.severity = warning\ndotnet_naming_rule.local_functions_rule.style = upper_camel_case_style\ndotnet_naming_rule.local_functions_rule.symbols = local_functions_symbols\ndotnet_naming_rule.method_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.method_rule.severity = warning\ndotnet_naming_rule.method_rule.style = upper_camel_case_style\ndotnet_naming_rule.method_rule.symbols = method_symbols\ndotnet_naming_rule.parameters_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.parameters_rule.severity = warning\ndotnet_naming_rule.parameters_rule.style = lower_camel_case_style_1\ndotnet_naming_rule.parameters_rule.symbols = parameters_symbols\ndotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.private_constants_rule.severity = warning\ndotnet_naming_rule.private_constants_rule.style = upper_camel_case_style\ndotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols\ndotnet_naming_rule.private_instance_fields_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.private_instance_fields_rule.severity = warning\ndotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style\ndotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols\ndotnet_naming_rule.private_static_fields_override_rule.import_to_resharper = False\ndotnet_naming_rule.private_static_fields_override_rule.severity = warning\ndotnet_naming_rule.private_static_fields_override_rule.style = upper_camel_case_style\ndotnet_naming_rule.private_static_fields_override_rule.symbols = private_static_fields_override_symbols\ndotnet_naming_rule.private_static_fields_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.private_static_fields_rule.severity = warning\ndotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style\ndotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols\ndotnet_naming_rule.private_static_fields_rule_1.import_to_resharper = True\ndotnet_naming_rule.private_static_fields_rule_1.resharper_description = PrivateStaticFields\ndotnet_naming_rule.private_static_fields_rule_1.resharper_guid = ee7c9d0d-12d0-427b-bf48-38a6b1740690\ndotnet_naming_rule.private_static_fields_rule_1.severity = warning\ndotnet_naming_rule.private_static_fields_rule_1.style = lower_camel_case_style\ndotnet_naming_rule.private_static_fields_rule_1.symbols = private_static_fields_symbols_1\ndotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.private_static_readonly_rule.severity = warning\ndotnet_naming_rule.private_static_readonly_rule.style = lower_camel_case_style\ndotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols\ndotnet_naming_rule.property_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.property_rule.severity = warning\ndotnet_naming_rule.property_rule.style = upper_camel_case_style\ndotnet_naming_rule.property_rule.symbols = property_symbols\ndotnet_naming_rule.public_fields_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.public_fields_rule.severity = warning\ndotnet_naming_rule.public_fields_rule.style = lower_camel_case_style\ndotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols\ndotnet_naming_rule.static_readonly_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.static_readonly_rule.severity = warning\ndotnet_naming_rule.static_readonly_rule.style = lower_camel_case_style\ndotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols\ndotnet_naming_rule.types_and_namespaces_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.types_and_namespaces_rule.severity = warning\ndotnet_naming_rule.types_and_namespaces_rule.style = upper_camel_case_style\ndotnet_naming_rule.types_and_namespaces_rule.symbols = types_and_namespaces_symbols\ndotnet_naming_rule.type_parameters_rule.import_to_resharper = as_predefined\ndotnet_naming_rule.type_parameters_rule.resharper_style = T + AaBb, V + AaBb, K + AaBb\ndotnet_naming_rule.type_parameters_rule.severity = warning\ndotnet_naming_rule.type_parameters_rule.style = t_upper_camel_case_style\ndotnet_naming_rule.type_parameters_rule.symbols = type_parameters_symbols\ndotnet_naming_style.i_upper_camel_case_style.capitalization = pascal_case\ndotnet_naming_style.i_upper_camel_case_style.required_prefix = I\ndotnet_naming_style.lower_camel_case_style.capitalization = camel_case\ndotnet_naming_style.lower_camel_case_style.required_prefix = _\ndotnet_naming_style.lower_camel_case_style_1.capitalization = camel_case\ndotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case\ndotnet_naming_style.t_upper_camel_case_style.required_prefix = T\ndotnet_naming_style.upper_camel_case_style.capitalization = pascal_case\ndotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected\ndotnet_naming_symbols.constants_symbols.applicable_kinds = field\ndotnet_naming_symbols.constants_symbols.required_modifiers = const\ndotnet_naming_symbols.event_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.event_symbols.applicable_kinds = event\ndotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface\ndotnet_naming_symbols.locals_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.locals_symbols.applicable_kinds = local\ndotnet_naming_symbols.local_constants_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.local_constants_symbols.applicable_kinds = local\ndotnet_naming_symbols.local_constants_symbols.required_modifiers = const\ndotnet_naming_symbols.local_constants_symbols_1.applicable_accessibilities = *\ndotnet_naming_symbols.local_constants_symbols_1.applicable_kinds = local\ndotnet_naming_symbols.local_constants_symbols_1.required_modifiers = const\ndotnet_naming_symbols.local_constants_symbols_1.resharper_applicable_kinds = local_constant\ndotnet_naming_symbols.local_constants_symbols_1.resharper_required_modifiers = any\ndotnet_naming_symbols.local_functions_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.local_functions_symbols.applicable_kinds = local_function\ndotnet_naming_symbols.method_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.method_symbols.applicable_kinds = method\ndotnet_naming_symbols.parameters_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.parameters_symbols.applicable_kinds = parameter\ndotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private\ndotnet_naming_symbols.private_constants_symbols.applicable_kinds = field\ndotnet_naming_symbols.private_constants_symbols.required_modifiers = const\ndotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private\ndotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field\ndotnet_naming_symbols.private_static_fields_override_symbols.applicable_accessibilities = local,private\ndotnet_naming_symbols.private_static_fields_override_symbols.applicable_kinds = field\ndotnet_naming_symbols.private_static_fields_override_symbols.required_modifiers = const,static\ndotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private\ndotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field\ndotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static\ndotnet_naming_symbols.private_static_fields_symbols_1.applicable_accessibilities = local,private\ndotnet_naming_symbols.private_static_fields_symbols_1.applicable_kinds = field\ndotnet_naming_symbols.private_static_fields_symbols_1.required_modifiers = static\ndotnet_naming_symbols.private_static_fields_symbols_1.resharper_applicable_kinds = field\ndotnet_naming_symbols.private_static_fields_symbols_1.resharper_required_modifiers = static\ndotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private\ndotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field\ndotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly\ndotnet_naming_symbols.property_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.property_symbols.applicable_kinds = property\ndotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected\ndotnet_naming_symbols.public_fields_symbols.applicable_kinds = field\ndotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected\ndotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field\ndotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly\ndotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate\ndotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = *\ndotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter\ndotnet_separate_import_directive_groups = false\ndotnet_sort_system_directives_first = true\ndotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none\ndotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none\ndotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:none\ndotnet_style_predefined_type_for_locals_parameters_members = true:warning\ndotnet_style_predefined_type_for_member_access = true:warning\ndotnet_style_qualification_for_event = false:suggestion\ndotnet_style_qualification_for_field = false:suggestion\ndotnet_style_qualification_for_method = false:suggestion\ndotnet_style_qualification_for_property = false:suggestion\ndotnet_style_require_accessibility_modifiers = for_non_interface_members:warning\nfile_header_template = \n\n# ReSharper properties\nresharper_accessor_owner_body = expression_body\nresharper_alignment_tab_fill_style = use_spaces\nresharper_align_first_arg_by_paren = false\nresharper_align_linq_query = false\nresharper_align_multiline_array_and_object_initializer = false\nresharper_align_multiline_array_initializer = true\nresharper_align_multiline_ctor_init = true\nresharper_align_multiline_expression_braces = false\nresharper_align_multiline_implements_list = true\nresharper_align_multiline_property_pattern = false\nresharper_align_multiline_switch_expression = false\nresharper_align_multiline_type_argument = true\nresharper_align_multiline_type_parameter = true\nresharper_align_multline_type_parameter_constrains = false\nresharper_align_multline_type_parameter_list = false\nresharper_align_ternary = align_not_nested\nresharper_align_tuple_components = false\nresharper_allow_alias = true\nresharper_allow_comment_after_lbrace = true\nresharper_allow_far_alignment = false\nresharper_always_use_end_of_line_brace_style = false\nresharper_apply_auto_detected_rules = false\nresharper_apply_on_completion = false\nresharper_arguments_anonymous_function = positional\nresharper_arguments_literal = positional\nresharper_arguments_named = positional\nresharper_arguments_other = positional\nresharper_arguments_skip_single = false\nresharper_arguments_string_literal = positional\nresharper_attribute_style = do_not_touch\nresharper_autodetect_indent_settings = false\nresharper_blank_lines_after_block_statements = 1\nresharper_blank_lines_after_case = 0\nresharper_blank_lines_after_control_transfer_statements = 0\nresharper_blank_lines_after_imports = 1\nresharper_blank_lines_after_multiline_statements = 0\nresharper_blank_lines_after_options = 1\nresharper_blank_lines_after_start_comment = 1\nresharper_blank_lines_after_using_list = 1\nresharper_blank_lines_around_accessor = 0\nresharper_blank_lines_around_auto_property = 1\nresharper_blank_lines_around_block_case_section = 0\nresharper_blank_lines_around_class_definition = 1\nresharper_blank_lines_around_field = 1\nresharper_blank_lines_around_function_declaration = 0\nresharper_blank_lines_around_function_definition = 1\nresharper_blank_lines_around_global_attribute = 0\nresharper_blank_lines_around_invocable = 1\nresharper_blank_lines_around_local_method = 1\nresharper_blank_lines_around_multiline_case_section = 0\nresharper_blank_lines_around_namespace = 1\nresharper_blank_lines_around_other_declaration = 0\nresharper_blank_lines_around_property = 1\nresharper_blank_lines_around_razor_functions = 1\nresharper_blank_lines_around_razor_helpers = 1\nresharper_blank_lines_around_razor_sections = 1\nresharper_blank_lines_around_single_line_accessor = 0\nresharper_blank_lines_around_single_line_auto_property = 0\nresharper_blank_lines_around_single_line_field = 0\nresharper_blank_lines_around_single_line_function_definition = 0\nresharper_blank_lines_around_single_line_invocable = 0\nresharper_blank_lines_around_single_line_local_method = 0\nresharper_blank_lines_around_single_line_property = 0\nresharper_blank_lines_around_single_line_type = 0\nresharper_blank_lines_around_type = 1\nresharper_blank_lines_before_block_statements = 0\nresharper_blank_lines_before_case = 0\nresharper_blank_lines_before_control_transfer_statements = 0\nresharper_blank_lines_before_multiline_statements = 0\nresharper_blank_lines_before_single_line_comment = 0\nresharper_blank_lines_inside_namespace = 0\nresharper_blank_lines_inside_region = 1\nresharper_blank_lines_inside_type = 0\nresharper_blank_line_after_pi = true\nresharper_braces_for_dowhile = required_for_multiline_statement\nresharper_braces_for_fixed = required\nresharper_braces_for_for = required_for_multiline_statement\nresharper_braces_for_foreach = required_for_multiline_statement\nresharper_braces_for_ifelse = required_for_multiline\nresharper_braces_for_lock = required_for_multiline_statement\nresharper_braces_for_using = required_for_multiline_statement\nresharper_braces_for_while = required_for_multiline_statement\nresharper_braces_redundant = false\nresharper_break_template_declaration = line_break\nresharper_can_use_global_alias = false\nresharper_configure_await_analysis_mode = disabled\nresharper_constructor_or_destructor_body = block_body\nresharper_continuous_indent_multiplier = 1\nresharper_continuous_line_indent = single\nresharper_cpp_align_multiline_argument = true\nresharper_cpp_align_multiline_binary_expressions_chain = true\nresharper_cpp_align_multiline_calls_chain = true\nresharper_cpp_align_multiline_extends_list = true\nresharper_cpp_align_multiline_for_stmt = true\nresharper_cpp_align_multiline_parameter = true\nresharper_cpp_align_multiple_declaration = true\nresharper_cpp_anonymous_method_declaration_braces = next_line\nresharper_cpp_case_block_braces = next_line_shifted_2\nresharper_cpp_empty_block_style = multiline\nresharper_cpp_indent_invocation_pars = inside\nresharper_cpp_indent_method_decl_pars = inside\nresharper_cpp_indent_statement_pars = inside\nresharper_cpp_indent_switch_labels = false\nresharper_cpp_insert_final_newline = false\nresharper_cpp_int_align_comments = false\nresharper_cpp_invocable_declaration_braces = next_line\nresharper_cpp_max_line_length = 160\nresharper_cpp_new_line_before_while = true\nresharper_cpp_other_braces = next_line\nresharper_cpp_space_after_cast = false\nresharper_cpp_space_around_binary_operator = true\nresharper_cpp_type_declaration_braces = next_line\nresharper_cpp_use_indent_from_previous_element = true\nresharper_cpp_wrap_lines = true\nresharper_cpp_wrap_multiple_type_parameter_constraints_style = chop_if_long\nresharper_cpp_wrap_parameters_style = wrap_if_long\nresharper_csharp_align_multiline_argument = false\nresharper_csharp_align_multiline_binary_expressions_chain = false\nresharper_csharp_align_multiline_calls_chain = false\nresharper_csharp_align_multiline_expression = false\nresharper_csharp_align_multiline_extends_list = false\nresharper_csharp_align_multiline_for_stmt = false\nresharper_csharp_align_multiline_parameter = false\nresharper_csharp_align_multiple_declaration = false\nresharper_csharp_blank_lines_around_region = 2\nresharper_csharp_case_block_braces = end_of_line\nresharper_csharp_empty_block_style = together_same_line\nresharper_csharp_indent_invocation_pars = outside_and_inside\nresharper_csharp_indent_method_decl_pars = outside_and_inside\nresharper_csharp_indent_statement_pars = outside_and_inside\nresharper_csharp_insert_final_newline = true\nresharper_csharp_int_align_comments = true\nresharper_csharp_max_line_length = 160\nresharper_csharp_naming_rule.constants = AaBb\nresharper_csharp_naming_rule.enum_member = AaBb\nresharper_csharp_naming_rule.local_constants = aaBb\nresharper_csharp_naming_rule.method_property_event = AaBb\nresharper_csharp_naming_rule.other = AaBb\nresharper_csharp_naming_rule.private_constants = AaBb\nresharper_csharp_naming_rule.private_static_fields = _ + aaBb\nresharper_csharp_naming_rule.private_static_readonly = _ + aaBb\nresharper_csharp_naming_rule.static_readonly = _ + aaBb\nresharper_csharp_new_line_before_while = false\nresharper_csharp_prefer_qualified_reference = false\nresharper_csharp_space_after_unary_operator = false\nresharper_csharp_stick_comment = false\nresharper_csharp_use_indent_from_previous_element = false\nresharper_csharp_wrap_lines = true\nresharper_csharp_wrap_multiple_type_parameter_constraints_style = wrap_if_long\nresharper_csharp_wrap_parameters_style = chop_if_long\nresharper_cxxcli_property_declaration_braces = next_line\nresharper_default_exception_variable_name = e\nresharper_default_value_when_type_evident = default_literal\nresharper_default_value_when_type_not_evident = default_expression\nresharper_delete_quotes_from_solid_values = false\nresharper_disable_blank_line_changes = false\nresharper_disable_formatter = false\nresharper_disable_indenter = false\nresharper_disable_int_align = false\nresharper_disable_line_break_changes = false\nresharper_disable_line_break_removal = false\nresharper_disable_space_changes = false\nresharper_disable_space_changes_before_trailing_comment = false\nresharper_dont_remove_extra_blank_lines = false\nresharper_enable_wrapping = false\nresharper_enforce_line_ending_style = false\nresharper_event_handler_pattern_long = $object$On$event$\nresharper_event_handler_pattern_short = On$event$\nresharper_expression_braces = inside\nresharper_expression_pars = inside\nresharper_extra_spaces = remove_all\nresharper_force_attribute_style = separate\nresharper_force_chop_compound_do_expression = false\nresharper_force_chop_compound_if_expression = false\nresharper_force_chop_compound_while_expression = false\nresharper_format_leading_spaces_decl = false\nresharper_free_block_braces = next_line\nresharper_function_declaration_return_type_style = do_not_change\nresharper_function_definition_return_type_style = do_not_change\nresharper_generator_mode = false\nresharper_html_attribute_indent = align_by_first_attribute\nresharper_html_insert_final_newline = true\nresharper_html_linebreak_before_elements = body,div,p,form,h1,h2,h3\nresharper_html_max_blank_lines_between_tags = 2\nresharper_html_max_line_length = 160\nresharper_html_pi_attributes_indent = align_by_first_attribute\nresharper_html_pi_attribute_style = on_single_line\nresharper_html_space_before_self_closing = false\nresharper_html_use_indent_from_previous_element = true\nresharper_html_wrap_lines = true\nresharper_ignore_space_preservation = false\nresharper_include_prefix_comment_in_indent = false\nresharper_indent_access_specifiers_from_class = false\nresharper_indent_aligned_ternary = true\nresharper_indent_anonymous_method_block = false\nresharper_indent_case_from_select = true\nresharper_indent_child_elements = OneIndent\nresharper_indent_class_members_from_access_specifiers = false\nresharper_indent_comment = true\nresharper_indent_inside_namespace = true\nresharper_indent_nested_fixed_stmt = false\nresharper_indent_nested_foreach_stmt = true\nresharper_indent_nested_for_stmt = true\nresharper_indent_nested_lock_stmt = true\nresharper_indent_nested_usings_stmt = false\nresharper_indent_nested_while_stmt = true\nresharper_indent_pars = outside_and_inside\nresharper_indent_preprocessor_directives = none\nresharper_indent_preprocessor_if = no_indent\nresharper_indent_preprocessor_other = no_indent\nresharper_indent_preprocessor_region = usual_indent\nresharper_indent_text = OneIndent\nresharper_indent_typearg_angles = outside_and_inside\nresharper_indent_typeparam_angles = outside_and_inside\nresharper_indent_type_constraints = true\nresharper_indent_wrapped_function_names = false\nresharper_instance_members_qualify_declared_in = \nresharper_int_align_assignments = false\nresharper_int_align_binary_expressions = false\nresharper_int_align_declaration_names = false\nresharper_int_align_eq = false\nresharper_int_align_fields = false\nresharper_int_align_fix_in_adjacent = true\nresharper_int_align_invocations = false\nresharper_int_align_methods = false\nresharper_int_align_nested_ternary = false\nresharper_int_align_parameters = false\nresharper_int_align_properties = false\nresharper_int_align_property_patterns = false\nresharper_int_align_switch_expressions = false\nresharper_int_align_switch_sections = false\nresharper_int_align_variables = false\nresharper_keep_blank_lines_in_code = 2\nresharper_keep_blank_lines_in_declarations = 2\nresharper_keep_existing_attribute_arrangement = false\nresharper_keep_existing_declaration_block_arrangement = false\nresharper_keep_existing_declaration_parens_arrangement = true\nresharper_keep_existing_embedded_arrangement = true\nresharper_keep_existing_embedded_block_arrangement = false\nresharper_keep_existing_enum_arrangement = false\nresharper_keep_existing_expr_member_arrangement = true\nresharper_keep_existing_invocation_parens_arrangement = false\nresharper_keep_existing_property_patterns_arrangement = true\nresharper_keep_existing_switch_expression_arrangement = false\nresharper_keep_nontrivial_alias = true\nresharper_keep_user_linebreaks = true\nresharper_keep_user_wrapping = true\nresharper_linebreaks_around_razor_statements = true\nresharper_linebreaks_inside_tags_for_elements_longer_than = 2147483647\nresharper_linebreaks_inside_tags_for_elements_with_child_elements = true\nresharper_linebreaks_inside_tags_for_multiline_elements = true\nresharper_linebreak_before_all_elements = false\nresharper_linebreak_before_multiline_elements = true\nresharper_linebreak_before_singleline_elements = false\nresharper_line_break_after_colon_in_member_initializer_lists = do_not_change\nresharper_line_break_after_comma_in_member_initializer_lists = false\nresharper_line_break_before_comma_in_member_initializer_lists = false\nresharper_line_break_before_requires_clause = do_not_change\nresharper_linkage_specification_braces = end_of_line\nresharper_linkage_specification_indentation = none\nresharper_local_function_body = block_body\nresharper_macro_block_begin = \nresharper_macro_block_end = \nresharper_max_array_initializer_elements_on_line = 10000\nresharper_max_attribute_length_for_same_line = 38\nresharper_max_enum_members_on_line = 1\nresharper_max_formal_parameters_on_line = 10000\nresharper_max_initializer_elements_on_line = 4\nresharper_max_invocation_arguments_on_line = 10000\nresharper_member_initializer_list_style = do_not_change\nresharper_method_or_operator_body = block_body\nresharper_namespace_declaration_braces = next_line\nresharper_namespace_indentation = all\nresharper_nested_ternary_style = autodetect\nresharper_new_line_before_catch = true\nresharper_new_line_before_else = true\nresharper_new_line_before_enumerators = true\nresharper_normalize_tag_names = false\nresharper_no_indent_inside_elements = html,body,thead,tbody,tfoot\nresharper_no_indent_inside_if_element_longer_than = 200\nresharper_object_creation_when_type_evident = target_typed\nresharper_object_creation_when_type_not_evident = explicitly_typed\nresharper_old_engine = false\nresharper_outdent_binary_ops = false\nresharper_outdent_commas = false\nresharper_outdent_dots = false\nresharper_outdent_namespace_member = false\nresharper_outdent_statement_labels = false\nresharper_outdent_ternary_ops = false\nresharper_parentheses_non_obvious_operations = none, bitwise, bitwise_inclusive_or, bitwise_exclusive_or, shift, bitwise_and\nresharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence\nresharper_parentheses_same_type_operations = true\nresharper_place_accessorholder_attribute_on_same_line = if_owner_is_single_line\nresharper_place_accessor_attribute_on_same_line = true\nresharper_place_comments_at_first_column = false\nresharper_place_constructor_initializer_on_same_line = true\nresharper_place_event_attribute_on_same_line = false\nresharper_place_expr_accessor_on_single_line = true\nresharper_place_expr_method_on_single_line = true\nresharper_place_expr_property_on_single_line = true\nresharper_place_field_attribute_on_same_line = if_owner_is_single_line\nresharper_place_linq_into_on_new_line = false\nresharper_place_method_attribute_on_same_line = false\nresharper_place_namespace_definitions_on_same_line = false\nresharper_place_property_attribute_on_same_line = false\nresharper_place_simple_case_statement_on_same_line = if_owner_is_single_line\nresharper_place_simple_embedded_statement_on_same_line = if_owner_is_single_line\nresharper_place_simple_initializer_on_single_line = true\nresharper_place_simple_property_pattern_on_single_line = true\nresharper_place_simple_switch_expression_on_single_line = false\nresharper_place_type_attribute_on_same_line = false\nresharper_place_type_constraints_on_same_line = true\nresharper_prefer_explicit_discard_declaration = true\nresharper_prefer_separate_deconstructed_variables_declaration = false\nresharper_preserve_spaces_inside_tags = pre,textarea\nresharper_qualified_using_at_nested_scope = false\nresharper_quote_style = doublequoted\nresharper_razor_prefer_qualified_reference = true\nresharper_remove_blank_lines_near_braces = false\nresharper_remove_blank_lines_near_braces_in_code = true\nresharper_remove_blank_lines_near_braces_in_declarations = true\nresharper_remove_this_qualifier = true\nresharper_requires_expression_braces = next_line\nresharper_resx_attribute_indent = single_indent\nresharper_resx_insert_final_newline = false\nresharper_resx_linebreak_before_elements = \nresharper_resx_max_blank_lines_between_tags = 0\nresharper_resx_max_line_length = 2147483647\nresharper_resx_pi_attributes_indent = align_by_first_attribute\nresharper_resx_pi_attribute_style = do_not_touch\nresharper_resx_space_before_self_closing = false\nresharper_resx_use_indent_from_previous_element = true\nresharper_resx_wrap_lines = false\nresharper_resx_wrap_tags_and_pi = false\nresharper_resx_wrap_text = false\nresharper_show_autodetect_configure_formatting_tip = false\nresharper_simple_block_style = do_not_change\nresharper_simple_case_statement_style = do_not_change\nresharper_simple_embedded_statement_style = do_not_change\nresharper_sort_attributes = false\nresharper_sort_class_selectors = false\nresharper_sort_usings = true\nresharper_sort_usings_lowercase_first = false\nresharper_spaces_around_eq_in_attribute = false\nresharper_spaces_around_eq_in_pi_attribute = false\nresharper_spaces_inside_tags = false\nresharper_space_after_attributes = true\nresharper_space_after_attribute_target_colon = true\nresharper_space_after_cast = false\nresharper_space_after_colon = true\nresharper_space_after_colon_in_case = true\nresharper_space_after_colon_in_inheritance_clause = true\nresharper_space_after_comma = true\nresharper_space_after_for_colon = true\nresharper_space_after_keywords_in_control_flow_statements = true\nresharper_space_after_last_attribute = false\nresharper_space_after_last_pi_attribute = false\nresharper_space_after_operator_keyword = true\nresharper_space_after_ptr_in_data_member = true\nresharper_space_after_ptr_in_data_members = false\nresharper_space_after_ptr_in_method = true\nresharper_space_after_ref_in_data_member = true\nresharper_space_after_ref_in_data_members = false\nresharper_space_after_ref_in_method = true\nresharper_space_after_semicolon_in_for_statement = true\nresharper_space_after_ternary_colon = true\nresharper_space_after_ternary_quest = true\nresharper_space_after_triple_slash = true\nresharper_space_after_type_parameter_constraint_colon = true\nresharper_space_around_additive_op = true\nresharper_space_around_alias_eq = true\nresharper_space_around_assignment_op = true\nresharper_space_around_assignment_operator = true\nresharper_space_around_deref_in_trailing_return_type = true\nresharper_space_around_lambda_arrow = true\nresharper_space_around_member_access_operator = false\nresharper_space_around_relational_op = true\nresharper_space_around_shift_op = true\nresharper_space_around_stmt_colon = true\nresharper_space_around_ternary_operator = true\nresharper_space_before_array_rank_parentheses = false\nresharper_space_before_attribute_target_colon = false\nresharper_space_before_checked_parentheses = false\nresharper_space_before_colon = false\nresharper_space_before_colon_in_case = false\nresharper_space_before_colon_in_inheritance_clause = true\nresharper_space_before_comma = false\nresharper_space_before_default_parentheses = false\nresharper_space_before_empty_invocation_parentheses = false\nresharper_space_before_empty_method_parentheses = false\nresharper_space_before_for_colon = true\nresharper_space_before_initializer_braces = false\nresharper_space_before_invocation_parentheses = false\nresharper_space_before_label_colon = false\nresharper_space_before_lambda_parentheses = false\nresharper_space_before_method_parentheses = false\nresharper_space_before_nameof_parentheses = false\nresharper_space_before_new_parentheses = false\nresharper_space_before_nullable_mark = false\nresharper_space_before_open_square_brackets = false\nresharper_space_before_pointer_asterik_declaration = false\nresharper_space_before_ptr_in_abstract_decl = false\nresharper_space_before_ptr_in_data_member = false\nresharper_space_before_ptr_in_data_members = true\nresharper_space_before_ptr_in_method = false\nresharper_space_before_ref_in_abstract_decl = false\nresharper_space_before_ref_in_data_member = false\nresharper_space_before_ref_in_data_members = true\nresharper_space_before_ref_in_method = false\nresharper_space_before_semicolon = false\nresharper_space_before_semicolon_in_for_statement = false\nresharper_space_before_singleline_accessorholder = true\nresharper_space_before_sizeof_parentheses = false\nresharper_space_before_template_args = false\nresharper_space_before_template_params = true\nresharper_space_before_ternary_colon = true\nresharper_space_before_ternary_quest = true\nresharper_space_before_trailing_comment = true\nresharper_space_before_typeof_parentheses = false\nresharper_space_before_type_argument_angle = false\nresharper_space_before_type_parameter_angle = false\nresharper_space_before_type_parameter_constraint_colon = true\nresharper_space_before_type_parameter_parentheses = true\nresharper_space_between_accessors_in_singleline_property = true\nresharper_space_between_attribute_sections = true\nresharper_space_between_closing_angle_brackets_in_template_args = false\nresharper_space_between_keyword_and_expression = true\nresharper_space_between_keyword_and_type = true\nresharper_space_between_method_call_empty_parameter_list_parentheses = false\nresharper_space_between_method_call_name_and_opening_parenthesis = false\nresharper_space_between_method_call_parameter_list_parentheses = false\nresharper_space_between_method_declaration_empty_parameter_list_parentheses = false\nresharper_space_between_method_declaration_name_and_open_parenthesis = false\nresharper_space_between_method_declaration_parameter_list_parentheses = false\nresharper_space_between_parentheses_of_control_flow_statements = false\nresharper_space_between_square_brackets = false\nresharper_space_between_typecast_parentheses = false\nresharper_space_in_singleline_accessorholder = true\nresharper_space_in_singleline_anonymous_method = true\nresharper_space_in_singleline_method = true\nresharper_space_near_postfix_and_prefix_op = false\nresharper_space_within_array_initialization_braces = false\nresharper_space_within_array_rank_empty_parentheses = false\nresharper_space_within_array_rank_parentheses = false\nresharper_space_within_attribute_angles = false\nresharper_space_within_checked_parentheses = false\nresharper_space_within_default_parentheses = false\nresharper_space_within_empty_braces = true\nresharper_space_within_empty_initializer_braces = false\nresharper_space_within_empty_invocation_parentheses = false\nresharper_space_within_empty_method_parentheses = false\nresharper_space_within_empty_template_params = false\nresharper_space_within_expression_parentheses = false\nresharper_space_within_initializer_braces = false\nresharper_space_within_invocation_parentheses = false\nresharper_space_within_method_parentheses = false\nresharper_space_within_nameof_parentheses = false\nresharper_space_within_new_parentheses = false\nresharper_space_within_parentheses = false\nresharper_space_within_single_line_array_initializer_braces = true\nresharper_space_within_sizeof_parentheses = false\nresharper_space_within_template_args = false\nresharper_space_within_template_params = false\nresharper_space_within_tuple_parentheses = false\nresharper_space_within_typeof_parentheses = false\nresharper_space_within_type_argument_angles = false\nresharper_space_within_type_parameter_angles = false\nresharper_space_within_type_parameter_parentheses = false\nresharper_special_else_if_treatment = true\nresharper_static_members_qualify_members = none\nresharper_static_members_qualify_with = declared_type\nresharper_support_vs_event_naming_pattern = true\nresharper_toplevel_function_declaration_return_type_style = do_not_change\nresharper_toplevel_function_definition_return_type_style = do_not_change\nresharper_trailing_comma_in_multiline_lists = true\nresharper_trailing_comma_in_singleline_lists = false\nresharper_use_continuous_indent_inside_initializer_braces = true\nresharper_use_continuous_indent_inside_parens = true\nresharper_use_continuous_line_indent_in_expression_braces = false\nresharper_use_continuous_line_indent_in_method_pars = false\nresharper_use_heuristics_for_body_style = true\nresharper_use_indents_from_main_language_in_file = true\nresharper_use_indent_from_vs = false\nresharper_use_roslyn_logic_for_evident_types = false\nresharper_vb_align_multiline_argument = true\nresharper_vb_align_multiline_expression = true\nresharper_vb_align_multiline_parameter = true\nresharper_vb_align_multiple_declaration = true\nresharper_vb_blank_lines_around_region = 1\nresharper_vb_insert_final_newline = false\nresharper_vb_max_line_length = 160\nresharper_vb_place_field_attribute_on_same_line = true\nresharper_vb_place_method_attribute_on_same_line = false\nresharper_vb_place_type_attribute_on_same_line = false\nresharper_vb_prefer_qualified_reference = false\nresharper_vb_space_after_unary_operator = true\nresharper_vb_space_around_multiplicative_op = false\nresharper_vb_stick_comment = true\nresharper_vb_use_indent_from_previous_element = true\nresharper_vb_wrap_lines = true\nresharper_vb_wrap_parameters_style = wrap_if_long\nresharper_wrap_after_binary_opsign = true\nresharper_wrap_after_declaration_lpar = false\nresharper_wrap_after_dot = false\nresharper_wrap_after_dot_in_method_calls = false\nresharper_wrap_after_expression_lbrace = true\nresharper_wrap_after_invocation_lpar = false\nresharper_wrap_arguments_style = wrap_if_long\nresharper_wrap_around_elements = true\nresharper_wrap_array_initializer_style = chop_if_long\nresharper_wrap_base_clause_style = wrap_if_long\nresharper_wrap_before_arrow_with_expressions = false\nresharper_wrap_before_binary_opsign = true\nresharper_wrap_before_colon = false\nresharper_wrap_before_comma = false\nresharper_wrap_before_comma_in_base_clause = false\nresharper_wrap_before_declaration_lpar = false\nresharper_wrap_before_declaration_rpar = false\nresharper_wrap_before_eq = false\nresharper_wrap_before_expression_rbrace = true\nresharper_wrap_before_extends_colon = false\nresharper_wrap_before_first_type_parameter_constraint = false\nresharper_wrap_before_invocation_lpar = false\nresharper_wrap_before_invocation_rpar = false\nresharper_wrap_before_linq_expression = false\nresharper_wrap_before_ternary_opsigns = true\nresharper_wrap_before_type_parameter_langle = false\nresharper_wrap_braced_init_list_style = wrap_if_long\nresharper_wrap_chained_binary_expressions = chop_if_long\nresharper_wrap_chained_method_calls = wrap_if_long\nresharper_wrap_ctor_initializer_style = wrap_if_long\nresharper_wrap_enumeration_style = chop_if_long\nresharper_wrap_enum_declaration = chop_always\nresharper_wrap_extends_list_style = wrap_if_long\nresharper_wrap_for_stmt_header_style = wrap_if_long\nresharper_wrap_multiple_declaration_style = chop_if_long\nresharper_wrap_object_and_collection_initializer_style = chop_if_long\nresharper_wrap_property_pattern = chop_if_long\nresharper_wrap_switch_expression = chop_always\nresharper_wrap_ternary_expr_style = chop_if_long\nresharper_wrap_verbatim_interpolated_strings = no_wrap\nresharper_xmldoc_attribute_indent = double_indent\nresharper_xmldoc_insert_final_newline = false\nresharper_xmldoc_linebreak_before_elements = summary,remarks,example,returns,param,typeparam,value,para\nresharper_xmldoc_max_blank_lines_between_tags = 0\nresharper_xmldoc_max_line_length = 160\nresharper_xmldoc_pi_attributes_indent = double_indent\nresharper_xmldoc_pi_attribute_style = do_not_touch\nresharper_xmldoc_space_before_self_closing = true\nresharper_xmldoc_use_indent_from_previous_element = true\nresharper_xmldoc_wrap_lines = true\nresharper_xmldoc_wrap_tags_and_pi = false\nresharper_xmldoc_wrap_text = true\nresharper_xml_attribute_indent = align_by_first_attribute\nresharper_xml_insert_final_newline = false\nresharper_xml_linebreak_before_elements = \nresharper_xml_max_blank_lines_between_tags = 2\nresharper_xml_max_line_length = 160\nresharper_xml_pi_attributes_indent = align_by_first_attribute\nresharper_xml_pi_attribute_style = do_not_touch\nresharper_xml_space_before_self_closing = true\nresharper_xml_use_indent_from_previous_element = true\nresharper_xml_wrap_lines = true\nresharper_xml_wrap_tags_and_pi = true\nresharper_xml_wrap_text = false\n\n# ReSharper inspection severities\nresharper_access_rights_in_text_highlighting = warning\nresharper_access_to_disposed_closure_highlighting = warning\nresharper_access_to_for_each_variable_in_closure_highlighting = warning\nresharper_access_to_modified_closure_highlighting = warning\nresharper_access_to_static_member_via_derived_type_highlighting = warning\nresharper_address_of_marshal_by_ref_object_highlighting = warning\nresharper_angular_html_banana_highlighting = warning\nresharper_annotate_can_be_null_parameter_highlighting = none\nresharper_annotate_can_be_null_type_member_highlighting = none\nresharper_annotate_not_null_parameter_highlighting = none\nresharper_annotate_not_null_type_member_highlighting = none\nresharper_annotation_conflict_in_hierarchy_highlighting = warning\nresharper_annotation_redundancy_at_value_type_highlighting = warning\nresharper_annotation_redundancy_in_hierarchy_highlighting = warning\nresharper_arguments_style_anonymous_function_highlighting = hint\nresharper_arguments_style_literal_highlighting = hint\nresharper_arguments_style_named_expression_highlighting = hint\nresharper_arguments_style_other_highlighting = hint\nresharper_arguments_style_string_literal_highlighting = hint\nresharper_arrange_accessor_owner_body_highlighting = suggestion\nresharper_arrange_attributes_highlighting = warning\nresharper_arrange_constructor_or_destructor_body_highlighting = none\nresharper_arrange_default_value_when_type_evident_highlighting = suggestion\nresharper_arrange_default_value_when_type_not_evident_highlighting = warning\nresharper_arrange_local_function_body_highlighting = none\nresharper_arrange_method_or_operator_body_highlighting = none\nresharper_arrange_missing_parentheses_highlighting = warning\nresharper_arrange_object_creation_when_type_evident_highlighting = suggestion\nresharper_arrange_object_creation_when_type_not_evident_highlighting = warning\nresharper_arrange_redundant_parentheses_highlighting = none\nresharper_arrange_static_member_qualifier_highlighting = hint\nresharper_arrange_this_qualifier_highlighting = hint\nresharper_arrange_trailing_comma_in_multiline_lists_highlighting = warning\nresharper_arrange_trailing_comma_in_singleline_lists_highlighting = warning\nresharper_arrange_var_keywords_in_deconstructing_declaration_highlighting = suggestion\nresharper_asp_content_placeholder_not_resolved_highlighting = error\nresharper_asp_custom_page_parser_filter_type_highlighting = warning\nresharper_asp_dead_code_highlighting = warning\nresharper_asp_entity_highlighting = warning\nresharper_asp_image_highlighting = warning\nresharper_asp_invalid_control_type_highlighting = error\nresharper_asp_not_resolved_highlighting = error\nresharper_asp_ods_method_reference_resolve_error_highlighting = error\nresharper_asp_resolve_warning_highlighting = warning\nresharper_asp_skin_not_resolved_highlighting = error\nresharper_asp_tag_attribute_with_optional_value_highlighting = warning\nresharper_asp_theme_not_resolved_highlighting = error\nresharper_asp_unused_register_directive_highlighting_highlighting = warning\nresharper_asp_warning_highlighting = warning\nresharper_assignment_in_conditional_expression_highlighting = warning\nresharper_assignment_is_fully_discarded_highlighting = warning\nresharper_assign_null_to_not_null_attribute_highlighting = hint\nresharper_asxx_path_error_highlighting = warning\nresharper_async_iterator_invocation_without_await_foreach_highlighting = warning\nresharper_async_void_lambda_highlighting = warning\nresharper_async_void_method_highlighting = none\nresharper_auto_property_can_be_made_get_only_global_highlighting = suggestion\nresharper_auto_property_can_be_made_get_only_local_highlighting = suggestion\nresharper_bad_attribute_brackets_spaces_highlighting = none\nresharper_bad_braces_spaces_highlighting = none\nresharper_bad_child_statement_indent_highlighting = warning\nresharper_bad_colon_spaces_highlighting = none\nresharper_bad_comma_spaces_highlighting = none\nresharper_bad_control_braces_indent_highlighting = suggestion\nresharper_bad_control_braces_line_breaks_highlighting = none\nresharper_bad_declaration_braces_indent_highlighting = none\nresharper_bad_declaration_braces_line_breaks_highlighting = none\nresharper_bad_empty_braces_line_breaks_highlighting = none\nresharper_bad_expression_braces_indent_highlighting = none\nresharper_bad_expression_braces_line_breaks_highlighting = none\nresharper_bad_generic_brackets_spaces_highlighting = none\nresharper_bad_indent_highlighting = none\nresharper_bad_linq_line_breaks_highlighting = none\nresharper_bad_list_line_breaks_highlighting = none\nresharper_bad_member_access_spaces_highlighting = none\nresharper_bad_namespace_braces_indent_highlighting = none\nresharper_bad_parens_line_breaks_highlighting = none\nresharper_bad_parens_spaces_highlighting = none\nresharper_bad_preprocessor_indent_highlighting = none\nresharper_bad_semicolon_spaces_highlighting = none\nresharper_bad_spaces_after_keyword_highlighting = none\nresharper_bad_square_brackets_spaces_highlighting = none\nresharper_bad_switch_braces_indent_highlighting = none\nresharper_bad_symbol_spaces_highlighting = none\nresharper_base_member_has_params_highlighting = warning\nresharper_base_method_call_with_default_parameter_highlighting = warning\nresharper_base_object_equals_is_object_equals_highlighting = warning\nresharper_base_object_get_hash_code_call_in_get_hash_code_highlighting = warning\nresharper_bitwise_operator_on_enum_without_flags_highlighting = warning\nresharper_by_ref_argument_is_volatile_field_highlighting = warning\nresharper_cannot_apply_equality_operator_to_type_highlighting = warning\nresharper_center_tag_is_obsolete_highlighting = warning\nresharper_check_for_reference_equality_instead_1_highlighting = suggestion\nresharper_check_for_reference_equality_instead_2_highlighting = suggestion\nresharper_check_for_reference_equality_instead_3_highlighting = suggestion\nresharper_check_for_reference_equality_instead_4_highlighting = suggestion\nresharper_check_namespace_highlighting = warning\nresharper_class_cannot_be_instantiated_highlighting = warning\nresharper_class_can_be_sealed_global_highlighting = none\nresharper_class_can_be_sealed_local_highlighting = none\nresharper_class_never_instantiated_global_highlighting = suggestion\nresharper_class_never_instantiated_local_highlighting = suggestion\nresharper_class_with_virtual_members_never_inherited_global_highlighting = suggestion\nresharper_class_with_virtual_members_never_inherited_local_highlighting = suggestion\nresharper_clear_attribute_is_obsolete_all_highlighting = warning\nresharper_clear_attribute_is_obsolete_highlighting = warning\nresharper_collection_never_queried_global_highlighting = warning\nresharper_collection_never_queried_local_highlighting = warning\nresharper_collection_never_updated_global_highlighting = warning\nresharper_collection_never_updated_local_highlighting = warning\nresharper_comment_typo_highlighting = hint\nresharper_compare_non_constrained_generic_with_null_highlighting = none\nresharper_compare_of_floats_by_equality_operator_highlighting = warning\nresharper_conditional_ternary_equal_branch_highlighting = warning\nresharper_condition_is_always_true_or_false_highlighting = warning\nresharper_confusing_char_as_integer_in_constructor_highlighting = warning\nresharper_constant_conditional_access_qualifier_highlighting = warning\nresharper_constant_null_coalescing_condition_highlighting = warning\nresharper_constructor_initializer_loop_highlighting = warning\nresharper_container_annotation_redundancy_highlighting = warning\nresharper_context_value_is_provided_highlighting = none\nresharper_contract_annotation_not_parsed_highlighting = warning\nresharper_convert_closure_to_method_group_highlighting = suggestion\nresharper_convert_conditional_ternary_expression_to_switch_expression_highlighting = hint\nresharper_convert_if_do_to_while_highlighting = suggestion\nresharper_convert_if_statement_to_conditional_ternary_expression_highlighting = suggestion\nresharper_convert_if_statement_to_null_coalescing_assignment_highlighting = suggestion\nresharper_convert_if_statement_to_null_coalescing_expression_highlighting = suggestion\nresharper_convert_if_statement_to_return_statement_highlighting = hint\nresharper_convert_if_statement_to_switch_expression_highlighting = hint\nresharper_convert_if_statement_to_switch_statement_highlighting = hint\nresharper_convert_if_to_or_expression_highlighting = suggestion\nresharper_convert_nullable_to_short_form_highlighting = suggestion\nresharper_convert_switch_statement_to_switch_expression_highlighting = hint\nresharper_convert_to_auto_property_highlighting = suggestion\nresharper_convert_to_auto_property_when_possible_highlighting = hint\nresharper_convert_to_auto_property_with_private_setter_highlighting = hint\nresharper_convert_to_compound_assignment_highlighting = hint\nresharper_convert_to_constant_global_highlighting = hint\nresharper_convert_to_constant_local_highlighting = hint\nresharper_convert_to_lambda_expression_highlighting = suggestion\nresharper_convert_to_lambda_expression_when_possible_highlighting = none\nresharper_convert_to_local_function_highlighting = suggestion\nresharper_convert_to_null_coalescing_compound_assignment_highlighting = suggestion\nresharper_convert_to_static_class_highlighting = suggestion\nresharper_convert_to_using_declaration_highlighting = suggestion\nresharper_convert_to_vb_auto_property_highlighting = suggestion\nresharper_convert_to_vb_auto_property_when_possible_highlighting = hint\nresharper_convert_to_vb_auto_property_with_private_setter_highlighting = hint\nresharper_co_variant_array_conversion_highlighting = warning\nresharper_cpp_abstract_class_without_specifier_highlighting = warning\nresharper_cpp_abstract_virtual_function_call_in_ctor_highlighting = error\nresharper_cpp_access_specifier_with_no_declarations_highlighting = suggestion\nresharper_cpp_assigned_value_is_never_used_highlighting = warning\nresharper_cpp_awaiter_type_is_not_class_highlighting = warning\nresharper_cpp_bad_angle_brackets_spaces_highlighting = none\nresharper_cpp_bad_braces_spaces_highlighting = none\nresharper_cpp_bad_child_statement_indent_highlighting = none\nresharper_cpp_bad_colon_spaces_highlighting = none\nresharper_cpp_bad_comma_spaces_highlighting = none\nresharper_cpp_bad_control_braces_indent_highlighting = none\nresharper_cpp_bad_control_braces_line_breaks_highlighting = none\nresharper_cpp_bad_declaration_braces_indent_highlighting = none\nresharper_cpp_bad_declaration_braces_line_breaks_highlighting = none\nresharper_cpp_bad_empty_braces_line_breaks_highlighting = none\nresharper_cpp_bad_expression_braces_indent_highlighting = none\nresharper_cpp_bad_expression_braces_line_breaks_highlighting = none\nresharper_cpp_bad_indent_highlighting = none\nresharper_cpp_bad_list_line_breaks_highlighting = none\nresharper_cpp_bad_member_access_spaces_highlighting = none\nresharper_cpp_bad_namespace_braces_indent_highlighting = none\nresharper_cpp_bad_parens_line_breaks_highlighting = none\nresharper_cpp_bad_parens_spaces_highlighting = none\nresharper_cpp_bad_semicolon_spaces_highlighting = none\nresharper_cpp_bad_spaces_after_keyword_highlighting = none\nresharper_cpp_bad_square_brackets_spaces_highlighting = none\nresharper_cpp_bad_switch_braces_indent_highlighting = none\nresharper_cpp_bad_symbol_spaces_highlighting = none\nresharper_cpp_boolean_increment_expression_highlighting = warning\nresharper_cpp_boost_format_bad_code_highlighting = warning\nresharper_cpp_boost_format_legacy_code_highlighting = suggestion\nresharper_cpp_boost_format_mixed_args_highlighting = error\nresharper_cpp_boost_format_too_few_args_highlighting = error\nresharper_cpp_boost_format_too_many_args_highlighting = warning\nresharper_cpp_clang_tidy_abseil_duration_addition_highlighting = none\nresharper_cpp_clang_tidy_abseil_duration_comparison_highlighting = none\nresharper_cpp_clang_tidy_abseil_duration_conversion_cast_highlighting = none\nresharper_cpp_clang_tidy_abseil_duration_division_highlighting = none\nresharper_cpp_clang_tidy_abseil_duration_factory_float_highlighting = none\nresharper_cpp_clang_tidy_abseil_duration_factory_scale_highlighting = none\nresharper_cpp_clang_tidy_abseil_duration_subtraction_highlighting = none\nresharper_cpp_clang_tidy_abseil_duration_unnecessary_conversion_highlighting = none\nresharper_cpp_clang_tidy_abseil_faster_strsplit_delimiter_highlighting = none\nresharper_cpp_clang_tidy_abseil_no_internal_dependencies_highlighting = none\nresharper_cpp_clang_tidy_abseil_no_namespace_highlighting = none\nresharper_cpp_clang_tidy_abseil_redundant_strcat_calls_highlighting = none\nresharper_cpp_clang_tidy_abseil_string_find_startswith_highlighting = none\nresharper_cpp_clang_tidy_abseil_string_find_str_contains_highlighting = none\nresharper_cpp_clang_tidy_abseil_str_cat_append_highlighting = none\nresharper_cpp_clang_tidy_abseil_time_comparison_highlighting = none\nresharper_cpp_clang_tidy_abseil_time_subtraction_highlighting = none\nresharper_cpp_clang_tidy_abseil_upgrade_duration_conversions_highlighting = none\nresharper_cpp_clang_tidy_altera_kernel_name_restriction_highlighting = none\nresharper_cpp_clang_tidy_altera_single_work_item_barrier_highlighting = none\nresharper_cpp_clang_tidy_altera_struct_pack_align_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_accept4_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_accept_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_creat_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_dup_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_epoll_create1_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_epoll_create_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_fopen_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_inotify_init1_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_inotify_init_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_memfd_create_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_open_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_pipe2_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_pipe_highlighting = none\nresharper_cpp_clang_tidy_android_cloexec_socket_highlighting = none\nresharper_cpp_clang_tidy_android_comparison_in_temp_failure_retry_highlighting = none\nresharper_cpp_clang_tidy_boost_use_to_string_highlighting = suggestion\nresharper_cpp_clang_tidy_bugprone_argument_comment_highlighting = suggestion\nresharper_cpp_clang_tidy_bugprone_assert_side_effect_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_bad_signal_to_kill_thread_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_bool_pointer_implicit_conversion_highlighting = none\nresharper_cpp_clang_tidy_bugprone_branch_clone_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_copy_constructor_init_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_dangling_handle_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_dynamic_static_initializers_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_exception_escape_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_fold_init_type_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_forwarding_reference_overload_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_forward_declaration_namespace_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_inaccurate_erase_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_incorrect_roundings_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_infinite_loop_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_integer_division_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_lambda_function_name_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_macro_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_macro_repeated_side_effects_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_misplaced_operator_in_strlen_in_alloc_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_misplaced_pointer_arithmetic_in_alloc_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_misplaced_widening_cast_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_move_forwarding_reference_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_multiple_statement_macro_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_narrowing_conversions_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_not_null_terminated_result_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_no_escape_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_parent_virtual_call_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_posix_return_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_redundant_branch_condition_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_reserved_identifier_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_signal_handler_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_signed_char_misuse_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_sizeof_container_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_sizeof_expression_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_spuriously_wake_up_functions_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_string_constructor_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_string_integer_assignment_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_string_literal_with_embedded_nul_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_suspicious_enum_usage_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_suspicious_include_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_suspicious_memset_usage_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_suspicious_missing_comma_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_suspicious_semicolon_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_suspicious_string_compare_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_swapped_arguments_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_terminating_continue_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_throw_keyword_missing_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_too_small_loop_variable_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_undefined_memory_manipulation_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_undelegated_constructor_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_unhandled_self_assignment_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_unused_raii_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_unused_return_value_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_use_after_move_highlighting = warning\nresharper_cpp_clang_tidy_bugprone_virtual_near_miss_highlighting = suggestion\nresharper_cpp_clang_tidy_cert_con36_c_highlighting = none\nresharper_cpp_clang_tidy_cert_con54_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl03_c_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl16_c_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl21_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl37_c_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl50_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl51_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl54_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_dcl58_cpp_highlighting = warning\nresharper_cpp_clang_tidy_cert_dcl59_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_env33_c_highlighting = none\nresharper_cpp_clang_tidy_cert_err09_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_err34_c_highlighting = suggestion\nresharper_cpp_clang_tidy_cert_err52_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_err58_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_err60_cpp_highlighting = warning\nresharper_cpp_clang_tidy_cert_err61_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_fio38_c_highlighting = none\nresharper_cpp_clang_tidy_cert_flp30_c_highlighting = warning\nresharper_cpp_clang_tidy_cert_mem57_cpp_highlighting = warning\nresharper_cpp_clang_tidy_cert_msc30_c_highlighting = none\nresharper_cpp_clang_tidy_cert_msc32_c_highlighting = none\nresharper_cpp_clang_tidy_cert_msc50_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_msc51_cpp_highlighting = warning\nresharper_cpp_clang_tidy_cert_oop11_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_oop54_cpp_highlighting = none\nresharper_cpp_clang_tidy_cert_oop57_cpp_highlighting = warning\nresharper_cpp_clang_tidy_cert_oop58_cpp_highlighting = warning\nresharper_cpp_clang_tidy_cert_pos44_c_highlighting = none\nresharper_cpp_clang_tidy_cert_sig30_c_highlighting = none\nresharper_cpp_clang_tidy_cert_str34_c_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_api_modeling_google_g_test_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_api_modeling_llvm_cast_value_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_api_modeling_llvm_return_value_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_api_modeling_std_c_library_functions_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_api_modeling_trust_nonnull_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_builtin_builtin_functions_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_builtin_no_return_functions_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_call_and_message_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_call_and_message_modeling_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_divide_zero_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_dynamic_type_propagation_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_nonnil_string_constants_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_non_null_param_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_null_dereference_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_stack_address_escape_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_stack_addr_escape_base_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_undefined_binary_operator_result_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_array_subscript_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_assign_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_branch_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_captured_block_variable_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_undef_return_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_core_vla_size_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_inner_pointer_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_move_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_new_delete_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_new_delete_leaks_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_placement_new_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_pure_virtual_call_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_self_assignment_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_smart_ptr_modeling_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_cplusplus_virtual_call_modeling_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_deadcode_dead_stores_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_fuchsia_handle_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_nullability_nullability_base_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_dereferenced_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_passed_to_nonnull_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_returned_from_nonnull_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_nullability_null_passed_to_nonnull_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_nullability_null_returned_from_nonnull_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_cplusplus_uninitialized_object_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_cplusplus_virtual_call_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_mpi_mpi_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_osx_cocoa_localizability_empty_localization_context_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_osx_cocoa_localizability_non_localized_string_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_osx_os_object_c_style_cast_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_performance_gcd_antipattern_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_performance_padding_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_optin_portability_unix_api_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_api_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_at_sync_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_autorelease_write_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_class_release_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_dealloc_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_incompatible_method_types_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_loops_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_missing_super_call_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_nil_arg_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_non_nil_return_value_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_ns_autorelease_pool_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_ns_error_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_obj_c_generics_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_retain_count_base_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_retain_count_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_run_loop_autorelease_leak_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_self_init_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_super_dealloc_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_unused_ivars_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_variadic_method_types_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_error_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_number_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_retain_release_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_containers_out_of_bounds_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_containers_pointer_sized_values_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_mig_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_ns_or_cf_error_deref_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_number_object_conversion_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_obj_c_property_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_os_object_retain_count_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_osx_sec_keychain_api_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_float_loop_counter_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bcmp_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bcopy_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bzero_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_decode_value_of_obj_c_type_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_deprecated_or_unsafe_buffer_handling_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_getpw_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_gets_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_mkstemp_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_mktemp_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_rand_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_security_syntax_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_strcpy_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_unchecked_return_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_vfork_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_api_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_cstring_bad_size_arg_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_cstring_c_string_modeling_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_cstring_null_arg_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_dynamic_memory_modeling_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_malloc_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_malloc_sizeof_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_mismatched_deallocator_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_unix_vfork_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_valist_copy_to_self_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_valist_uninitialized_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_valist_unterminated_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_valist_valist_base_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_webkit_no_uncounted_member_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_webkit_ref_cntbl_base_virtual_dtor_highlighting = none\nresharper_cpp_clang_tidy_clang_analyzer_webkit_uncounted_lambda_captures_checker_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_absolute_value_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_abstract_final_class_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_abstract_vbase_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_address_of_packed_member_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_address_of_temporary_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_alloca_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_alloca_with_align_alignof_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ambiguous_delete_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ambiguous_ellipsis_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ambiguous_macro_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ambiguous_member_template_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ambiguous_reversed_operator_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_analyzer_incompatible_plugin_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_anonymous_pack_parens_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_anon_enum_enum_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_arc_bridge_casts_disallowed_in_nonarc_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_arc_maybe_repeated_use_of_weak_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_arc_non_pod_memaccess_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_arc_perform_selector_leaks_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_arc_repeated_use_of_weak_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_arc_retain_cycles_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_arc_unsafe_retained_assign_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_argument_outside_range_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_array_bounds_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_array_bounds_pointer_arithmetic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_asm_operand_widths_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_assign_enum_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_assume_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_atimport_in_framework_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_atomic_alignment_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_atomic_implicit_seq_cst_highlighting = suggestion\nresharper_cpp_clang_tidy_clang_diagnostic_atomic_memory_ordering_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_atomic_property_with_user_defined_accessor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_attribute_packed_for_bitfield_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_at_protocol_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_auto_disable_vptr_sanitizer_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_auto_import_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_auto_storage_class_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_auto_var_id_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_availability_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_avr_rtlib_linking_quirks_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_backslash_newline_escape_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bad_function_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_binding_in_condition_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bind_to_temporary_copy_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bitfield_constant_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bitfield_enum_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bitfield_width_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bitwise_conditional_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bitwise_op_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_block_capture_autoreleasing_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bool_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bool_operation_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_braced_scalar_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_bridge_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_builtin_assume_aligned_alignment_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_builtin_macro_redefined_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_builtin_memcpy_chk_size_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_builtin_requires_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_c11_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_c2x_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_c99_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_c99_designator_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_c99_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_called_once_parameter_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_call_to_pure_virtual_from_ctor_dtor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cast_align_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cast_calling_convention_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cast_of_sel_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cast_qual_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cast_qual_unrelated_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cf_string_literal_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_char_subscripts_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_clang_cl_pch_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_class_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_class_varargs_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cmse_union_leak_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_comma_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_comment_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_compare_distinct_pointer_types_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_completion_handler_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_complex_component_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_by_macro_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_by_space_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_concepts_ts_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_conditional_type_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_conditional_uninitialized_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_config_macros_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_constant_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_constant_evaluated_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_constant_logical_operand_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_constexpr_not_const_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_consumed_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_coroutine_missing_unhandled_exception_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_covered_switch_default_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_deprecated_writable_strings_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_reserved_user_defined_literal_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_extra_semi_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_inline_namespace_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_long_long_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp11_narrowing_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp14_binary_literal_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp14_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp14_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_mangling_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp17_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp20_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp20_designator_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp20_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp2a_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_bind_to_temporary_copy_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_extra_semi_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_local_type_template_args_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_pedantic_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_unnamed_type_template_args_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_binary_literal_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_pedantic_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_compat_pedantic_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_cpp17_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_cpp17_compat_pedantic_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cpp_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cstring_format_directive_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ctad_maybe_unsupported_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_ctu_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_cuda_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_custom_atomic_properties_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dangling_else_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dangling_field_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dangling_gsl_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dangling_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dangling_initializer_list_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_darwin_sdk_settings_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_date_time_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dealloc_in_category_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_debug_compression_unavailable_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_declaration_after_statement_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_defaulted_function_deleted_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_delegating_ctor_cycles_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_delete_abstract_non_virtual_dtor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_delete_incomplete_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_delete_non_abstract_non_virtual_dtor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_delete_non_virtual_dtor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_anon_enum_enum_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_array_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_attributes_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_comma_subscript_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_dtor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_declarations_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_dynamic_exception_spec_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_compare_conditional_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_enum_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_float_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_implementations_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_increment_bool_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_isa_usage_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_pointer_introspection_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_pointer_introspection_perform_selector_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_register_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_this_capture_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_deprecated_volatile_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_direct_ivar_access_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_disabled_macro_expansion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_distributed_object_modifiers_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_division_by_zero_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dllexport_explicit_instantiation_decl_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dllimport_static_field_def_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dll_attribute_on_redeclaration_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_documentation_deprecated_sync_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_documentation_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_documentation_html_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_documentation_pedantic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_documentation_unknown_command_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_dollar_in_identifier_extension_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_double_promotion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dtor_name_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dtor_typedef_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_duplicate_decl_specifier_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_duplicate_enum_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_duplicate_method_arg_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_duplicate_method_match_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_duplicate_protocol_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dynamic_class_memaccess_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_dynamic_exception_spec_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_embedded_directive_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_empty_body_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_empty_decomposition_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_empty_init_stmt_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_empty_translation_unit_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_encode_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_enum_compare_conditional_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_enum_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_enum_compare_switch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_enum_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_enum_enum_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_enum_float_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_enum_too_large_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_error_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_exceptions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_excess_initializers_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_exit_time_destructors_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_expansion_to_defined_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_explicit_initialize_call_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_explicit_ownership_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_export_unnamed_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_export_using_directive_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_extern_c_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_extern_initializer_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_extra_qualification_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_extra_semi_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_extra_semi_stmt_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_extra_tokens_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_fallback_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_final_dtor_non_final_class_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_fixed_enum_extension_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_fixed_point_overflow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_flag_enum_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_flexible_array_extensions_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_float_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_float_equal_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_float_overflow_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_float_zero_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_extra_args_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_insufficient_args_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_invalid_specifier_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_nonliteral_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_non_iso_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_pedantic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_security_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_type_confusion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_format_zero_length_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_fortify_source_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_for_loop_analysis_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_four_char_constants_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_framework_include_private_from_public_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_frame_address_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_frame_larger_than_eq_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_free_nonheap_object_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_function_def_in_objc_container_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_function_multiversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gcc_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_global_constructors_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_global_isel_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_alignof_expression_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_anonymous_struct_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_array_member_paren_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_auto_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_binary_literal_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_case_range_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_complex_integer_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_compound_literal_initializer_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_conditional_omitted_operand_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_designator_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_empty_initializer_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_empty_struct_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_flexible_array_initializer_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_flexible_array_union_member_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_folding_constant_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_imaginary_constant_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_include_next_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_inline_cpp_without_extern_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_label_as_value_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_redeclared_enum_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_statement_expression_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_static_float_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_string_literal_operator_template_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_union_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_variable_sized_type_not_at_end_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_gnu_zero_variadic_macro_arguments_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_header_guard_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_header_hygiene_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_hip_only_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_idiomatic_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ignored_attributes_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_ignored_optimization_argument_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ignored_pragmas_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ignored_pragma_intrinsic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ignored_pragma_optimize_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_ignored_qualifiers_highlighting = suggestion\nresharper_cpp_clang_tidy_clang_diagnostic_implicitly_unsigned_literal_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_atomic_properties_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_const_int_float_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_conversion_floating_point_to_bool_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_exception_spec_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_fallthrough_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_fallthrough_per_function_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_fixed_point_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_float_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_function_declaration_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_int_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_int_float_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_int_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_implicit_retain_self_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_import_preprocessor_directive_pedantic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_inaccessible_base_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_include_next_absolute_path_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_include_next_outside_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_exception_spec_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_function_pointer_types_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_library_redeclaration_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_ms_struct_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_pointer_types_discards_qualifiers_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_pointer_types_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_property_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incompatible_sysroot_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incomplete_framework_module_declaration_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incomplete_implementation_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incomplete_module_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incomplete_setjmp_declaration_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_incomplete_umbrella_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_inconsistent_dllimport_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_inconsistent_missing_destructor_override_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_inconsistent_missing_override_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_increment_bool_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_independent_class_attribute_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_infinite_recursion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_initializer_overrides_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_injected_class_name_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_inline_asm_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_inline_namespace_reopened_noninline_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_inline_new_delete_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_instantiation_after_specialization_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_integer_overflow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_int_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_int_in_bool_context_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_int_to_pointer_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_int_to_void_pointer_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_constexpr_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_iboutlet_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_initializer_from_system_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_ios_deployment_target_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_noreturn_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_no_builtin_names_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_offsetof_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_or_nonexistent_directory_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_partial_specialization_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_pp_token_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_source_encoding_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_invalid_token_paste_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_jump_seh_finally_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_keyword_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_keyword_macro_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_knr_promoted_parameter_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_language_extension_token_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_large_by_value_copy_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_literal_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_literal_range_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_local_type_template_args_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_logical_not_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_logical_op_parentheses_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_long_long_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_macro_redefined_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_main_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_main_return_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_malformed_warning_check_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_many_braces_around_scalar_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_max_tokens_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_max_unsigned_zero_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_memset_transposed_args_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_memsize_comparison_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_method_signatures_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_anon_tag_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_charize_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_comment_paste_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_const_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_cpp_macro_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_default_arg_redefinition_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_drectve_section_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_end_of_file_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_enum_forward_reference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_enum_value_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_exception_spec_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_exists_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_explicit_constructor_call_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_extra_qualification_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_fixed_enum_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_flexible_array_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_goto_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_inaccessible_base_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_include_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_mutable_reference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_pure_definition_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_redeclare_static_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_sealed_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_template_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_template_shadow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_union_member_reference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_unqualified_friend_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_using_decl_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_microsoft_void_pseudo_dtor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_misleading_indentation_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_mismatched_new_delete_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_mismatched_parameter_types_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_mismatched_return_types_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_mismatched_tags_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_braces_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_constinit_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_declarations_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_exception_spec_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_field_initializers_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_method_return_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_noescape_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_noreturn_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_prototypes_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_missing_prototype_for_cc_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_selector_name_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_sysroot_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_missing_variable_declarations_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_misspelled_assumption_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_modules_ambiguous_internal_linkage_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_modules_import_nested_redundant_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_module_conflict_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_module_file_config_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_module_file_extension_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_module_import_in_extern_c_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_msvc_not_found_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_multichar_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_multiple_move_vbase_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nested_anon_types_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_newline_eof_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_new_returns_null_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_noderef_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nonnull_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nonportable_include_path_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nonportable_system_include_path_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nonportable_vector_initialization_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nontrivial_memaccess_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_non_c_typedef_for_linkage_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_non_literal_null_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_non_modular_include_in_framework_module_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_non_modular_include_in_module_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_non_pod_varargs_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_non_power_of_two_alignment_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_non_virtual_dtor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nsconsumed_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nsreturns_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ns_object_attribute_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nullability_completeness_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nullability_completeness_on_arrays_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nullability_declspec_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nullability_extension_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nullability_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nullability_inferred_on_nested_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_nullable_to_nonnull_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_null_arithmetic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_null_character_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_null_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_null_dereference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_null_pointer_arithmetic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_odr_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_old_style_cast_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_opencl_unsupported_rgba_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_openmp_clauses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_openmp_loop_form_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_openmp_mapping_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_openmp_target_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_option_ignored_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_ordered_compare_function_pointers_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_out_of_line_declaration_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_out_of_scope_function_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_overlength_strings_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_overloaded_shift_op_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_overloaded_virtual_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_override_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_override_module_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_overriding_method_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_overriding_t_option_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_over_aligned_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_packed_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_padded_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_parentheses_equality_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pass_failed_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pch_date_time_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pedantic_core_features_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pedantic_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pessimizing_move_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_arith_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_bool_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_integer_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_sign_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_to_enum_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_to_int_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pointer_type_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_poison_system_directories_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_potentially_evaluated_expression_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pragmas_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pragma_clang_attribute_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pragma_messages_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pragma_once_outside_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pragma_pack_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pragma_pack_suspicious_include_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_pragma_system_header_outside_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_predefined_identifier_outside_function_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_private_extern_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_private_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_private_module_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_profile_instr_missing_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_profile_instr_out_of_date_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_profile_instr_unprofiled_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_property_access_dot_syntax_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_property_attribute_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_protocol_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_protocol_property_synthesis_ambiguity_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_psabi_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_qualified_void_return_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_quoted_include_in_framework_header_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_range_loop_analysis_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_range_loop_bind_reference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_range_loop_construct_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_readonly_iboutlet_property_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_receiver_expr_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_receiver_forward_class_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_redeclared_class_member_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_redundant_move_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_redundant_parens_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_register_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_reinterpret_base_class_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_reorder_ctor_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_reorder_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_reorder_init_list_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_requires_expression_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_requires_super_attribute_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_reserved_id_macro_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_reserved_user_defined_literal_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_retained_language_linkage_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_return_stack_address_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_return_std_move_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_return_std_move_in_cpp11_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_return_type_c_linkage_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_return_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_rewrite_not_bool_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_section_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_selector_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_selector_type_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_self_assign_field_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_self_assign_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_self_assign_overloaded_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_self_move_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_semicolon_before_method_body_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sentinel_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_serialized_diagnostics_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shadow_field_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shadow_field_in_constructor_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_shadow_field_in_constructor_modified_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_shadow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shadow_ivar_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shadow_uncaptured_local_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_shift_count_negative_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shift_count_overflow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shift_negative_value_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shift_op_parentheses_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shift_overflow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shift_sign_overflow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_shorten64_to32_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_signed_enum_bitfield_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_signed_unsigned_wchar_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sign_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sign_conversion_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_argument_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_decay_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_div_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sizeof_pointer_div_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sizeof_pointer_memaccess_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_slash_u_filename_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_slh_asm_goto_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_sometimes_uninitialized_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_source_uses_openmp_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_spir_compat_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_static_float_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_static_inline_explicit_instantiation_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_static_in_inline_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_static_local_in_inline_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_static_self_init_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_stdlibcxx_not_found_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_strict_prototypes_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_strict_selector_match_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_string_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_string_concatenation_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_string_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_string_plus_char_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_string_plus_int_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_strlcpy_strlcat_size_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_strncat_size_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_suggest_destructor_override_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_suggest_override_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_super_class_method_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_suspicious_bzero_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_switch_bool_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_switch_enum_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_switch_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_sync_fetch_and_nand_semantics_changed_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_bitwise_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_in_range_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_out_of_range_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_objc_bool_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_overlap_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_pointer_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_type_limit_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_undefined_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_enum_zero_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_zero_compare_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_tautological_value_range_compare_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_tentative_definition_incomplete_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_thread_safety_analysis_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_thread_safety_attributes_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_thread_safety_beta_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_thread_safety_negative_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_thread_safety_precise_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_thread_safety_reference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_thread_safety_verbose_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_trigraphs_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_typedef_redefinition_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_typename_missing_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_type_safety_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unable_to_open_stats_file_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unavailable_declarations_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undeclared_selector_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undefined_bool_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undefined_func_template_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undefined_inline_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undefined_internal_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undefined_internal_type_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undefined_reinterpret_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undefined_var_template_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undef_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_undef_prefix_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_underaligned_exception_object_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unevaluated_expression_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unguarded_availability_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unguarded_availability_new_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unicode_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unicode_homoglyph_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unicode_whitespace_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unicode_zero_width_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_uninitialized_const_reference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_uninitialized_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unknown_argument_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unknown_attributes_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_unknown_cuda_version_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_unknown_escape_sequence_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unknown_pragmas_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_unknown_sanitizers_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unknown_warning_option_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unnamed_type_template_args_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unneeded_internal_declaration_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unneeded_member_function_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_break_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_loop_increment_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_return_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsequenced_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_abs_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_availability_guard_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_cb_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_dll_base_class_template_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_friend_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_gpopt_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_nan_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_target_opt_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_unsupported_visibility_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unusable_partial_specialization_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_comparison_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_const_variable_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_exception_parameter_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_function_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_getter_return_value_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_label_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_lambda_capture_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_unused_local_typedef_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_macros_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_member_function_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_parameter_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_unused_private_field_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_property_ivar_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_result_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_template_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_value_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_unused_variable_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_unused_volatile_lvalue_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_used_but_marked_unused_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_user_defined_literals_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_user_defined_warnings_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_varargs_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_variadic_macros_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_vector_conversion_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_vec_elem_size_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_vexing_parse_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_visibility_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_vla_extension_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_vla_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_void_pointer_to_enum_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_void_pointer_to_int_cast_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_void_ptr_dereference_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_warnings_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_wasm_exception_spec_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_weak_template_vtables_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_weak_vtables_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_writable_strings_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_xor_used_as_pow_highlighting = warning\nresharper_cpp_clang_tidy_clang_diagnostic_zero_as_null_pointer_constant_highlighting = none\nresharper_cpp_clang_tidy_clang_diagnostic_zero_length_array_highlighting = warning\nresharper_cpp_clang_tidy_concurrency_mt_unsafe_highlighting = warning\nresharper_cpp_clang_tidy_cppcoreguidelines_avoid_c_arrays_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_avoid_goto_highlighting = warning\nresharper_cpp_clang_tidy_cppcoreguidelines_avoid_magic_numbers_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_avoid_non_const_global_variables_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_c_copy_assignment_signature_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_explicit_virtual_functions_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_init_variables_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_interfaces_global_init_highlighting = warning\nresharper_cpp_clang_tidy_cppcoreguidelines_macro_usage_highlighting = warning\nresharper_cpp_clang_tidy_cppcoreguidelines_narrowing_conversions_highlighting = warning\nresharper_cpp_clang_tidy_cppcoreguidelines_non_private_member_variables_in_classes_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_no_malloc_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_owning_memory_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_array_to_pointer_decay_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_constant_array_index_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_pointer_arithmetic_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_type_const_cast_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_type_cstyle_cast_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_type_member_init_highlighting = warning\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_type_reinterpret_cast_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_type_static_cast_downcast_highlighting = suggestion\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_type_union_access_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_pro_type_vararg_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_slicing_highlighting = none\nresharper_cpp_clang_tidy_cppcoreguidelines_special_member_functions_highlighting = suggestion\nresharper_cpp_clang_tidy_darwin_avoid_spinlock_highlighting = none\nresharper_cpp_clang_tidy_darwin_dispatch_once_nonstatic_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_default_arguments_calls_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_default_arguments_declarations_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_header_anon_namespaces_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_multiple_inheritance_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_overloaded_operator_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_statically_constructed_objects_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_trailing_return_highlighting = none\nresharper_cpp_clang_tidy_fuchsia_virtual_inheritance_highlighting = none\nresharper_cpp_clang_tidy_google_build_explicit_make_pair_highlighting = none\nresharper_cpp_clang_tidy_google_build_namespaces_highlighting = none\nresharper_cpp_clang_tidy_google_build_using_namespace_highlighting = none\nresharper_cpp_clang_tidy_google_default_arguments_highlighting = none\nresharper_cpp_clang_tidy_google_explicit_constructor_highlighting = none\nresharper_cpp_clang_tidy_google_global_names_in_headers_highlighting = none\nresharper_cpp_clang_tidy_google_objc_avoid_nsobject_new_highlighting = none\nresharper_cpp_clang_tidy_google_objc_avoid_throwing_exception_highlighting = none\nresharper_cpp_clang_tidy_google_objc_function_naming_highlighting = none\nresharper_cpp_clang_tidy_google_objc_global_variable_declaration_highlighting = none\nresharper_cpp_clang_tidy_google_readability_avoid_underscore_in_googletest_name_highlighting = none\nresharper_cpp_clang_tidy_google_readability_braces_around_statements_highlighting = none\nresharper_cpp_clang_tidy_google_readability_casting_highlighting = none\nresharper_cpp_clang_tidy_google_readability_function_size_highlighting = none\nresharper_cpp_clang_tidy_google_readability_namespace_comments_highlighting = none\nresharper_cpp_clang_tidy_google_readability_todo_highlighting = none\nresharper_cpp_clang_tidy_google_runtime_int_highlighting = none\nresharper_cpp_clang_tidy_google_runtime_operator_highlighting = warning\nresharper_cpp_clang_tidy_google_upgrade_googletest_case_highlighting = suggestion\nresharper_cpp_clang_tidy_hicpp_avoid_c_arrays_highlighting = none\nresharper_cpp_clang_tidy_hicpp_avoid_goto_highlighting = warning\nresharper_cpp_clang_tidy_hicpp_braces_around_statements_highlighting = none\nresharper_cpp_clang_tidy_hicpp_deprecated_headers_highlighting = none\nresharper_cpp_clang_tidy_hicpp_exception_baseclass_highlighting = suggestion\nresharper_cpp_clang_tidy_hicpp_explicit_conversions_highlighting = none\nresharper_cpp_clang_tidy_hicpp_function_size_highlighting = none\nresharper_cpp_clang_tidy_hicpp_invalid_access_moved_highlighting = none\nresharper_cpp_clang_tidy_hicpp_member_init_highlighting = none\nresharper_cpp_clang_tidy_hicpp_move_const_arg_highlighting = none\nresharper_cpp_clang_tidy_hicpp_multiway_paths_covered_highlighting = warning\nresharper_cpp_clang_tidy_hicpp_named_parameter_highlighting = none\nresharper_cpp_clang_tidy_hicpp_new_delete_operators_highlighting = none\nresharper_cpp_clang_tidy_hicpp_noexcept_move_highlighting = none\nresharper_cpp_clang_tidy_hicpp_no_array_decay_highlighting = none\nresharper_cpp_clang_tidy_hicpp_no_assembler_highlighting = none\nresharper_cpp_clang_tidy_hicpp_no_malloc_highlighting = none\nresharper_cpp_clang_tidy_hicpp_signed_bitwise_highlighting = none\nresharper_cpp_clang_tidy_hicpp_special_member_functions_highlighting = none\nresharper_cpp_clang_tidy_hicpp_static_assert_highlighting = none\nresharper_cpp_clang_tidy_hicpp_undelegated_constructor_highlighting = none\nresharper_cpp_clang_tidy_hicpp_uppercase_literal_suffix_highlighting = none\nresharper_cpp_clang_tidy_hicpp_use_auto_highlighting = none\nresharper_cpp_clang_tidy_hicpp_use_emplace_highlighting = none\nresharper_cpp_clang_tidy_hicpp_use_equals_default_highlighting = none\nresharper_cpp_clang_tidy_hicpp_use_equals_delete_highlighting = none\nresharper_cpp_clang_tidy_hicpp_use_noexcept_highlighting = none\nresharper_cpp_clang_tidy_hicpp_use_nullptr_highlighting = none\nresharper_cpp_clang_tidy_hicpp_use_override_highlighting = none\nresharper_cpp_clang_tidy_hicpp_vararg_highlighting = none\nresharper_cpp_clang_tidy_highlighting_highlighting = suggestion\nresharper_cpp_clang_tidy_linuxkernel_must_check_errs_highlighting = warning\nresharper_cpp_clang_tidy_llvmlibc_callee_namespace_highlighting = none\nresharper_cpp_clang_tidy_llvmlibc_implementation_in_namespace_highlighting = none\nresharper_cpp_clang_tidy_llvmlibc_restrict_system_libc_headers_highlighting = none\nresharper_cpp_clang_tidy_llvm_else_after_return_highlighting = none\nresharper_cpp_clang_tidy_llvm_header_guard_highlighting = none\nresharper_cpp_clang_tidy_llvm_include_order_highlighting = none\nresharper_cpp_clang_tidy_llvm_namespace_comment_highlighting = none\nresharper_cpp_clang_tidy_llvm_prefer_isa_or_dyn_cast_in_conditionals_highlighting = none\nresharper_cpp_clang_tidy_llvm_prefer_register_over_unsigned_highlighting = suggestion\nresharper_cpp_clang_tidy_llvm_qualified_auto_highlighting = none\nresharper_cpp_clang_tidy_llvm_twine_local_highlighting = none\nresharper_cpp_clang_tidy_misc_definitions_in_headers_highlighting = none\nresharper_cpp_clang_tidy_misc_misplaced_const_highlighting = warning\nresharper_cpp_clang_tidy_misc_new_delete_overloads_highlighting = warning\nresharper_cpp_clang_tidy_misc_non_copyable_objects_highlighting = warning\nresharper_cpp_clang_tidy_misc_non_private_member_variables_in_classes_highlighting = none\nresharper_cpp_clang_tidy_misc_no_recursion_highlighting = none\nresharper_cpp_clang_tidy_misc_redundant_expression_highlighting = warning\nresharper_cpp_clang_tidy_misc_static_assert_highlighting = suggestion\nresharper_cpp_clang_tidy_misc_throw_by_value_catch_by_reference_highlighting = warning\nresharper_cpp_clang_tidy_misc_unconventional_assign_operator_highlighting = warning\nresharper_cpp_clang_tidy_misc_uniqueptr_reset_release_highlighting = suggestion\nresharper_cpp_clang_tidy_misc_unused_alias_decls_highlighting = suggestion\nresharper_cpp_clang_tidy_misc_unused_parameters_highlighting = none\nresharper_cpp_clang_tidy_misc_unused_using_decls_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_avoid_bind_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_avoid_c_arrays_highlighting = none\nresharper_cpp_clang_tidy_modernize_concat_nested_namespaces_highlighting = none\nresharper_cpp_clang_tidy_modernize_deprecated_headers_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_deprecated_ios_base_aliases_highlighting = warning\nresharper_cpp_clang_tidy_modernize_loop_convert_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_make_shared_highlighting = none\nresharper_cpp_clang_tidy_modernize_make_unique_highlighting = none\nresharper_cpp_clang_tidy_modernize_pass_by_value_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_raw_string_literal_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_redundant_void_arg_highlighting = none\nresharper_cpp_clang_tidy_modernize_replace_auto_ptr_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_replace_disallow_copy_and_assign_macro_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_replace_random_shuffle_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_return_braced_init_list_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_shrink_to_fit_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_unary_static_assert_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_auto_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_bool_literals_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_default_member_init_highlighting = none\nresharper_cpp_clang_tidy_modernize_use_emplace_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_equals_default_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_equals_delete_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_nodiscard_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_noexcept_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_nullptr_highlighting = none\nresharper_cpp_clang_tidy_modernize_use_override_highlighting = none\nresharper_cpp_clang_tidy_modernize_use_trailing_return_type_highlighting = none\nresharper_cpp_clang_tidy_modernize_use_transparent_functors_highlighting = suggestion\nresharper_cpp_clang_tidy_modernize_use_uncaught_exceptions_highlighting = warning\nresharper_cpp_clang_tidy_modernize_use_using_highlighting = none\nresharper_cpp_clang_tidy_mpi_buffer_deref_highlighting = warning\nresharper_cpp_clang_tidy_mpi_type_mismatch_highlighting = warning\nresharper_cpp_clang_tidy_objc_avoid_nserror_init_highlighting = warning\nresharper_cpp_clang_tidy_objc_dealloc_in_category_highlighting = warning\nresharper_cpp_clang_tidy_objc_forbidden_subclassing_highlighting = warning\nresharper_cpp_clang_tidy_objc_missing_hash_highlighting = warning\nresharper_cpp_clang_tidy_objc_nsinvocation_argument_lifetime_highlighting = warning\nresharper_cpp_clang_tidy_objc_property_declaration_highlighting = warning\nresharper_cpp_clang_tidy_objc_super_self_highlighting = warning\nresharper_cpp_clang_tidy_openmp_exception_escape_highlighting = warning\nresharper_cpp_clang_tidy_openmp_use_default_none_highlighting = warning\nresharper_cpp_clang_tidy_performance_faster_string_find_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_for_range_copy_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_implicit_conversion_in_loop_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_inefficient_algorithm_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_inefficient_string_concatenation_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_inefficient_vector_operation_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_move_constructor_init_highlighting = warning\nresharper_cpp_clang_tidy_performance_move_const_arg_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_noexcept_move_constructor_highlighting = none\nresharper_cpp_clang_tidy_performance_no_automatic_move_highlighting = warning\nresharper_cpp_clang_tidy_performance_no_int_to_ptr_highlighting = warning\nresharper_cpp_clang_tidy_performance_trivially_destructible_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_type_promotion_in_math_fn_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_unnecessary_copy_initialization_highlighting = suggestion\nresharper_cpp_clang_tidy_performance_unnecessary_value_param_highlighting = suggestion\nresharper_cpp_clang_tidy_portability_restrict_system_includes_highlighting = none\nresharper_cpp_clang_tidy_portability_simd_intrinsics_highlighting = none\nresharper_cpp_clang_tidy_readability_avoid_const_params_in_decls_highlighting = none\nresharper_cpp_clang_tidy_readability_braces_around_statements_highlighting = none\nresharper_cpp_clang_tidy_readability_const_return_type_highlighting = none\nresharper_cpp_clang_tidy_readability_container_size_empty_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_convert_member_functions_to_static_highlighting = none\nresharper_cpp_clang_tidy_readability_deleted_default_highlighting = none\nresharper_cpp_clang_tidy_readability_delete_null_pointer_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_else_after_return_highlighting = none\nresharper_cpp_clang_tidy_readability_function_cognitive_complexity_highlighting = none\nresharper_cpp_clang_tidy_readability_function_size_highlighting = none\nresharper_cpp_clang_tidy_readability_identifier_naming_highlighting = none\nresharper_cpp_clang_tidy_readability_implicit_bool_conversion_highlighting = none\nresharper_cpp_clang_tidy_readability_inconsistent_declaration_parameter_name_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_isolate_declaration_highlighting = none\nresharper_cpp_clang_tidy_readability_magic_numbers_highlighting = none\nresharper_cpp_clang_tidy_readability_make_member_function_const_highlighting = none\nresharper_cpp_clang_tidy_readability_misleading_indentation_highlighting = none\nresharper_cpp_clang_tidy_readability_misplaced_array_index_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_named_parameter_highlighting = none\nresharper_cpp_clang_tidy_readability_non_const_parameter_highlighting = none\nresharper_cpp_clang_tidy_readability_qualified_auto_highlighting = none\nresharper_cpp_clang_tidy_readability_redundant_access_specifiers_highlighting = none\nresharper_cpp_clang_tidy_readability_redundant_control_flow_highlighting = none\nresharper_cpp_clang_tidy_readability_redundant_declaration_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_redundant_function_ptr_dereference_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_redundant_member_init_highlighting = none\nresharper_cpp_clang_tidy_readability_redundant_preprocessor_highlighting = warning\nresharper_cpp_clang_tidy_readability_redundant_smartptr_get_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_redundant_string_cstr_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_redundant_string_init_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_simplify_boolean_expr_highlighting = none\nresharper_cpp_clang_tidy_readability_simplify_subscript_expr_highlighting = warning\nresharper_cpp_clang_tidy_readability_static_accessed_through_instance_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_static_definition_in_anonymous_namespace_highlighting = none\nresharper_cpp_clang_tidy_readability_string_compare_highlighting = warning\nresharper_cpp_clang_tidy_readability_uniqueptr_delete_release_highlighting = suggestion\nresharper_cpp_clang_tidy_readability_uppercase_literal_suffix_highlighting = none\nresharper_cpp_clang_tidy_readability_use_anyofallof_highlighting = suggestion\nresharper_cpp_clang_tidy_zircon_temporary_objects_highlighting = none\nresharper_cpp_class_can_be_final_highlighting = hint\nresharper_cpp_class_disallow_lazy_merging_highlighting = warning\nresharper_cpp_class_is_incomplete_highlighting = warning\nresharper_cpp_class_needs_constructor_because_of_uninitialized_member_highlighting = warning\nresharper_cpp_class_never_used_highlighting = warning\nresharper_cpp_compile_time_constant_can_be_replaced_with_boolean_constant_highlighting = suggestion\nresharper_cpp_const_parameter_in_declaration_highlighting = suggestion\nresharper_cpp_const_value_function_return_type_highlighting = suggestion\nresharper_cpp_coroutine_call_resolve_error_highlighting = warning\nresharper_cpp_cv_qualifier_can_not_be_applied_to_reference_highlighting = warning\nresharper_cpp_c_style_cast_highlighting = suggestion\nresharper_cpp_declaration_hides_local_highlighting = warning\nresharper_cpp_declaration_hides_uncaptured_local_highlighting = hint\nresharper_cpp_declaration_specifier_without_declarators_highlighting = warning\nresharper_cpp_declarator_disambiguated_as_function_highlighting = warning\nresharper_cpp_declarator_never_used_highlighting = warning\nresharper_cpp_declarator_used_before_initialization_highlighting = error\nresharper_cpp_defaulted_special_member_function_is_implicitly_deleted_highlighting = warning\nresharper_cpp_default_case_not_handled_in_switch_statement_highlighting = warning\nresharper_cpp_default_initialization_with_no_user_constructor_highlighting = warning\nresharper_cpp_default_is_used_as_identifier_highlighting = warning\nresharper_cpp_deleting_void_pointer_highlighting = warning\nresharper_cpp_dependent_template_without_template_keyword_highlighting = warning\nresharper_cpp_dependent_type_without_typename_keyword_highlighting = warning\nresharper_cpp_deprecated_entity_highlighting = warning\nresharper_cpp_deprecated_register_storage_class_specifier_highlighting = warning\nresharper_cpp_dereference_operator_limit_exceeded_highlighting = warning\nresharper_cpp_discarded_postfix_operator_result_highlighting = suggestion\nresharper_cpp_doxygen_syntax_error_highlighting = warning\nresharper_cpp_doxygen_undocumented_parameter_highlighting = suggestion\nresharper_cpp_doxygen_unresolved_reference_highlighting = warning\nresharper_cpp_empty_declaration_highlighting = warning\nresharper_cpp_enforce_cv_qualifiers_order_highlighting = none\nresharper_cpp_enforce_cv_qualifiers_placement_highlighting = none\nresharper_cpp_enforce_do_statement_braces_highlighting = none\nresharper_cpp_enforce_for_statement_braces_highlighting = none\nresharper_cpp_enforce_function_declaration_style_highlighting = none\nresharper_cpp_enforce_if_statement_braces_highlighting = none\nresharper_cpp_enforce_nested_namespaces_style_highlighting = hint\nresharper_cpp_enforce_overriding_destructor_style_highlighting = suggestion\nresharper_cpp_enforce_overriding_function_style_highlighting = suggestion\nresharper_cpp_enforce_type_alias_code_style_highlighting = none\nresharper_cpp_enforce_while_statement_braces_highlighting = none\nresharper_cpp_entity_assigned_but_no_read_highlighting = warning\nresharper_cpp_entity_used_only_in_unevaluated_context_highlighting = warning\nresharper_cpp_enumerator_never_used_highlighting = warning\nresharper_cpp_equal_operands_in_binary_expression_highlighting = warning\nresharper_cpp_explicit_specialization_in_non_namespace_scope_highlighting = warning\nresharper_cpp_expression_without_side_effects_highlighting = warning\nresharper_cpp_final_function_in_final_class_highlighting = suggestion\nresharper_cpp_functional_style_cast_highlighting = suggestion\nresharper_cpp_function_doesnt_return_value_highlighting = warning\nresharper_cpp_function_is_not_implemented_highlighting = warning\nresharper_cpp_header_has_been_already_included_highlighting = hint\nresharper_cpp_hidden_function_highlighting = warning\nresharper_cpp_hiding_function_highlighting = warning\nresharper_cpp_identical_operands_in_binary_expression_highlighting = warning\nresharper_cpp_if_can_be_replaced_by_constexpr_if_highlighting = suggestion\nresharper_cpp_implicit_default_constructor_not_available_highlighting = warning\nresharper_cpp_incompatible_pointer_conversion_highlighting = warning\nresharper_cpp_incomplete_switch_statement_highlighting = warning\nresharper_cpp_inconsistent_naming_highlighting = hint\nresharper_cpp_incorrect_blank_lines_near_braces_highlighting = none\nresharper_cpp_initialized_value_is_always_rewritten_highlighting = warning\nresharper_cpp_integral_to_pointer_conversion_highlighting = warning\nresharper_cpp_invalid_line_continuation_highlighting = warning\nresharper_cpp_join_declaration_and_assignment_highlighting = suggestion\nresharper_cpp_lambda_capture_never_used_highlighting = warning\nresharper_cpp_local_variable_may_be_const_highlighting = suggestion\nresharper_cpp_local_variable_might_not_be_initialized_highlighting = warning\nresharper_cpp_local_variable_with_non_trivial_dtor_is_never_used_highlighting = none\nresharper_cpp_long_float_highlighting = warning\nresharper_cpp_member_function_may_be_const_highlighting = suggestion\nresharper_cpp_member_function_may_be_static_highlighting = suggestion\nresharper_cpp_member_initializers_order_highlighting = suggestion\nresharper_cpp_mismatched_class_tags_highlighting = warning\nresharper_cpp_missing_blank_lines_highlighting = none\nresharper_cpp_missing_include_guard_highlighting = warning\nresharper_cpp_missing_indent_highlighting = none\nresharper_cpp_missing_linebreak_highlighting = none\nresharper_cpp_missing_space_highlighting = none\nresharper_cpp_ms_ext_address_of_class_r_value_highlighting = warning\nresharper_cpp_ms_ext_binding_r_value_to_lvalue_reference_highlighting = warning\nresharper_cpp_ms_ext_copy_elision_in_copy_init_declarator_highlighting = warning\nresharper_cpp_ms_ext_double_user_conversion_in_copy_init_highlighting = warning\nresharper_cpp_ms_ext_not_initialized_static_const_local_var_highlighting = warning\nresharper_cpp_ms_ext_reinterpret_cast_from_nullptr_highlighting = warning\nresharper_cpp_multiple_spaces_highlighting = none\nresharper_cpp_must_be_public_virtual_to_implement_interface_highlighting = warning\nresharper_cpp_mutable_specifier_on_reference_member_highlighting = warning\nresharper_cpp_non_exception_safe_resource_acquisition_highlighting = hint\nresharper_cpp_non_explicit_conversion_operator_highlighting = hint\nresharper_cpp_non_explicit_converting_constructor_highlighting = hint\nresharper_cpp_non_inline_function_definition_in_header_file_highlighting = warning\nresharper_cpp_non_inline_variable_definition_in_header_file_highlighting = warning\nresharper_cpp_not_all_paths_return_value_highlighting = warning\nresharper_cpp_no_discard_expression_highlighting = warning\nresharper_cpp_object_member_might_not_be_initialized_highlighting = warning\nresharper_cpp_outdent_is_off_prev_level_highlighting = none\nresharper_cpp_out_parameter_must_be_written_highlighting = warning\nresharper_cpp_parameter_may_be_const_highlighting = hint\nresharper_cpp_parameter_may_be_const_ptr_or_ref_highlighting = suggestion\nresharper_cpp_parameter_names_mismatch_highlighting = hint\nresharper_cpp_parameter_never_used_highlighting = hint\nresharper_cpp_parameter_value_is_reassigned_highlighting = warning\nresharper_cpp_pointer_conversion_drops_qualifiers_highlighting = warning\nresharper_cpp_pointer_to_integral_conversion_highlighting = warning\nresharper_cpp_polymorphic_class_with_non_virtual_public_destructor_highlighting = warning\nresharper_cpp_possibly_erroneous_empty_statements_highlighting = warning\nresharper_cpp_possibly_uninitialized_member_highlighting = warning\nresharper_cpp_possibly_unintended_object_slicing_highlighting = warning\nresharper_cpp_precompiled_header_is_not_included_highlighting = error\nresharper_cpp_precompiled_header_not_found_highlighting = error\nresharper_cpp_printf_bad_format_highlighting = warning\nresharper_cpp_printf_extra_arg_highlighting = warning\nresharper_cpp_printf_missed_arg_highlighting = error\nresharper_cpp_printf_risky_format_highlighting = warning\nresharper_cpp_private_special_member_function_is_not_implemented_highlighting = warning\nresharper_cpp_range_based_for_incompatible_reference_highlighting = warning\nresharper_cpp_redefinition_of_default_argument_in_override_function_highlighting = warning\nresharper_cpp_redundant_access_specifier_highlighting = hint\nresharper_cpp_redundant_base_class_access_specifier_highlighting = hint\nresharper_cpp_redundant_blank_lines_highlighting = none\nresharper_cpp_redundant_boolean_expression_argument_highlighting = warning\nresharper_cpp_redundant_cast_expression_highlighting = hint\nresharper_cpp_redundant_const_specifier_highlighting = hint\nresharper_cpp_redundant_control_flow_jump_highlighting = hint\nresharper_cpp_redundant_else_keyword_highlighting = hint\nresharper_cpp_redundant_else_keyword_inside_compound_statement_highlighting = hint\nresharper_cpp_redundant_empty_declaration_highlighting = hint\nresharper_cpp_redundant_empty_statement_highlighting = hint\nresharper_cpp_redundant_explicit_template_arguments_highlighting = hint\nresharper_cpp_redundant_inline_specifier_highlighting = hint\nresharper_cpp_redundant_linebreak_highlighting = none\nresharper_cpp_redundant_member_initializer_highlighting = suggestion\nresharper_cpp_redundant_parentheses_highlighting = hint\nresharper_cpp_redundant_qualifier_highlighting = hint\nresharper_cpp_redundant_space_highlighting = none\nresharper_cpp_redundant_static_specifier_on_member_allocation_function_highlighting = hint\nresharper_cpp_redundant_template_keyword_highlighting = warning\nresharper_cpp_redundant_typename_keyword_highlighting = warning\nresharper_cpp_redundant_void_argument_list_highlighting = suggestion\nresharper_cpp_reinterpret_cast_from_void_ptr_highlighting = suggestion\nresharper_cpp_remove_redundant_braces_highlighting = none\nresharper_cpp_replace_memset_with_zero_initialization_highlighting = suggestion\nresharper_cpp_replace_tie_with_structured_binding_highlighting = suggestion\nresharper_cpp_return_no_value_in_non_void_function_highlighting = warning\nresharper_cpp_smart_pointer_vs_make_function_highlighting = suggestion\nresharper_cpp_some_object_members_might_not_be_initialized_highlighting = warning\nresharper_cpp_special_function_without_noexcept_specification_highlighting = warning\nresharper_cpp_static_assert_failure_highlighting = error\nresharper_cpp_static_data_member_in_unnamed_struct_highlighting = warning\nresharper_cpp_static_specifier_on_anonymous_namespace_member_highlighting = suggestion\nresharper_cpp_syntax_warning_highlighting = warning\nresharper_cpp_tabs_and_spaces_mismatch_highlighting = none\nresharper_cpp_tabs_are_disallowed_highlighting = none\nresharper_cpp_tabs_outside_indent_highlighting = none\nresharper_cpp_template_parameter_shadowing_highlighting = warning\nresharper_cpp_this_arg_member_func_delegate_ctor_is_unsuported_by_dot_net_core_highlighting = none\nresharper_cpp_throw_expression_can_be_replaced_with_rethrow_highlighting = warning\nresharper_cpp_too_wide_scope_highlighting = suggestion\nresharper_cpp_too_wide_scope_init_statement_highlighting = hint\nresharper_cpp_type_alias_never_used_highlighting = warning\nresharper_cpp_ue4_blueprint_callable_function_may_be_const_highlighting = hint\nresharper_cpp_ue4_blueprint_callable_function_may_be_static_highlighting = hint\nresharper_cpp_ue4_coding_standard_naming_violation_warning_highlighting = hint\nresharper_cpp_ue4_coding_standard_u_class_naming_violation_error_highlighting = error\nresharper_cpp_ue4_probable_memory_issues_with_u_objects_in_container_highlighting = warning\nresharper_cpp_ue4_probable_memory_issues_with_u_object_highlighting = warning\nresharper_cpp_ue_incorrect_engine_directory_highlighting = error\nresharper_cpp_ue_non_existent_input_action_highlighting = warning\nresharper_cpp_ue_non_existent_input_axis_highlighting = warning\nresharper_cpp_ue_source_file_without_predefined_macros_highlighting = warning\nresharper_cpp_ue_source_file_without_standard_library_highlighting = error\nresharper_cpp_ue_version_file_doesnt_exist_highlighting = error\nresharper_cpp_uninitialized_dependent_base_class_highlighting = warning\nresharper_cpp_uninitialized_non_static_data_member_highlighting = warning\nresharper_cpp_union_member_of_reference_type_highlighting = warning\nresharper_cpp_unnamed_namespace_in_header_file_highlighting = warning\nresharper_cpp_unnecessary_whitespace_highlighting = none\nresharper_cpp_unreachable_code_highlighting = warning\nresharper_cpp_unsigned_zero_comparison_highlighting = warning\nresharper_cpp_unused_include_directive_highlighting = warning\nresharper_cpp_user_defined_literal_suffix_does_not_start_with_underscore_highlighting = warning\nresharper_cpp_use_algorithm_with_count_highlighting = suggestion\nresharper_cpp_use_auto_for_numeric_highlighting = hint\nresharper_cpp_use_auto_highlighting = hint\nresharper_cpp_use_elements_view_highlighting = suggestion\nresharper_cpp_use_familiar_template_syntax_for_generic_lambdas_highlighting = suggestion\nresharper_cpp_use_range_algorithm_highlighting = suggestion\nresharper_cpp_use_std_size_highlighting = suggestion\nresharper_cpp_use_structured_binding_highlighting = hint\nresharper_cpp_use_type_trait_alias_highlighting = suggestion\nresharper_cpp_using_result_of_assignment_as_condition_highlighting = warning\nresharper_cpp_u_function_macro_call_has_no_effect_highlighting = warning\nresharper_cpp_u_property_macro_call_has_no_effect_highlighting = warning\nresharper_cpp_variable_can_be_made_constexpr_highlighting = suggestion\nresharper_cpp_virtual_function_call_inside_ctor_highlighting = warning\nresharper_cpp_virtual_function_in_final_class_highlighting = warning\nresharper_cpp_volatile_parameter_in_declaration_highlighting = suggestion\nresharper_cpp_wrong_includes_order_highlighting = hint\nresharper_cpp_wrong_indent_size_highlighting = none\nresharper_cpp_wrong_slashes_in_include_directive_highlighting = hint\nresharper_cpp_zero_constant_can_be_replaced_with_nullptr_highlighting = suggestion\nresharper_cpp_zero_valued_expression_used_as_null_pointer_highlighting = warning\nresharper_c_declaration_with_implicit_int_type_highlighting = warning\nresharper_default_value_attribute_for_optional_parameter_highlighting = warning\nresharper_dl_tag_contains_non_dt_or_dd_elements_highlighting = hint\nresharper_double_negation_in_pattern_highlighting = suggestion\nresharper_double_negation_operator_highlighting = suggestion\nresharper_duplicate_resource_highlighting = warning\nresharper_dynamic_shift_right_op_is_not_int_highlighting = warning\nresharper_empty_constructor_highlighting = warning\nresharper_empty_destructor_highlighting = warning\nresharper_empty_embedded_statement_highlighting = warning\nresharper_empty_for_statement_highlighting = warning\nresharper_empty_general_catch_clause_highlighting = warning\nresharper_empty_namespace_highlighting = warning\nresharper_empty_statement_highlighting = warning\nresharper_empty_title_tag_highlighting = hint\nresharper_enforce_do_while_statement_braces_highlighting = warning\nresharper_enforce_fixed_statement_braces_highlighting = warning\nresharper_enforce_foreach_statement_braces_highlighting = warning\nresharper_enforce_for_statement_braces_highlighting = warning\nresharper_enforce_if_statement_braces_highlighting = warning\nresharper_enforce_lock_statement_braces_highlighting = warning\nresharper_enforce_using_statement_braces_highlighting = warning\nresharper_enforce_while_statement_braces_highlighting = warning\nresharper_entity_name_captured_only_global_highlighting = warning\nresharper_entity_name_captured_only_local_highlighting = warning\nresharper_enumerable_sum_in_explicit_unchecked_context_highlighting = warning\nresharper_enum_underlying_type_is_int_highlighting = warning\nresharper_equal_expression_comparison_highlighting = warning\nresharper_escaped_keyword_highlighting = warning\nresharper_event_never_invoked_global_highlighting = suggestion\nresharper_event_never_subscribed_to_global_highlighting = suggestion\nresharper_event_never_subscribed_to_local_highlighting = suggestion\nresharper_event_unsubscription_via_anonymous_delegate_highlighting = warning\nresharper_explicit_caller_info_argument_highlighting = warning\nresharper_expression_is_always_null_highlighting = warning\nresharper_field_can_be_made_read_only_global_highlighting = suggestion\nresharper_field_can_be_made_read_only_local_highlighting = suggestion\nresharper_field_hides_interface_property_with_default_implementation_highlighting = warning\nresharper_foreach_can_be_converted_to_query_using_another_get_enumerator_highlighting = hint\nresharper_foreach_can_be_partly_converted_to_query_using_another_get_enumerator_highlighting = hint\nresharper_format_string_placeholders_mismatch_highlighting = warning\nresharper_format_string_problem_highlighting = warning\nresharper_for_can_be_converted_to_foreach_highlighting = suggestion\nresharper_for_statement_condition_is_true_highlighting = warning\nresharper_function_complexity_overflow_highlighting = none\nresharper_function_never_returns_highlighting = warning\nresharper_function_recursive_on_all_paths_highlighting = warning\nresharper_gc_suppress_finalize_for_type_without_destructor_highlighting = warning\nresharper_generic_enumerator_not_disposed_highlighting = warning\nresharper_heuristic_unreachable_code_highlighting = warning\nresharper_html_attributes_quotes_highlighting = hint\nresharper_html_attribute_not_resolved_highlighting = warning\nresharper_html_attribute_value_not_resolved_highlighting = warning\nresharper_html_dead_code_highlighting = warning\nresharper_html_event_not_resolved_highlighting = warning\nresharper_html_id_duplication_highlighting = warning\nresharper_html_id_not_resolved_highlighting = warning\nresharper_html_obsolete_highlighting = warning\nresharper_html_path_error_highlighting = warning\nresharper_html_tag_not_closed_highlighting = error\nresharper_html_tag_not_resolved_highlighting = warning\nresharper_html_tag_should_be_self_closed_highlighting = warning\nresharper_html_tag_should_not_be_self_closed_highlighting = warning\nresharper_html_warning_highlighting = warning\nresharper_identifier_typo_highlighting = hint\nresharper_ignored_directive_highlighting = warning\nresharper_inactive_preprocessor_branch_highlighting = warning\nresharper_inconsistently_synchronized_field_highlighting = warning\nresharper_inconsistent_naming_highlighting = warning\nresharper_incorrect_blank_lines_near_braces_highlighting = none\nresharper_indexing_by_invalid_range_highlighting = warning\nresharper_inheritdoc_consider_usage_highlighting = none\nresharper_inheritdoc_invalid_usage_highlighting = warning\nresharper_inline_out_variable_declaration_highlighting = suggestion\nresharper_inline_temporary_variable_highlighting = hint\nresharper_internal_or_private_member_not_documented_highlighting = none\nresharper_interpolated_string_expression_is_not_i_formattable_highlighting = warning\nresharper_introduce_optional_parameters_global_highlighting = suggestion\nresharper_introduce_optional_parameters_local_highlighting = suggestion\nresharper_int_division_by_zero_highlighting = warning\nresharper_int_variable_overflow_highlighting = warning\nresharper_int_variable_overflow_in_checked_context_highlighting = warning\nresharper_int_variable_overflow_in_unchecked_context_highlighting = warning\nresharper_invalid_value_type_highlighting = warning\nresharper_invalid_xml_doc_comment_highlighting = warning\nresharper_invert_condition_1_highlighting = hint\nresharper_invert_if_highlighting = hint\nresharper_invocation_is_skipped_highlighting = hint\nresharper_invoke_as_extension_method_highlighting = suggestion\nresharper_is_expression_always_false_highlighting = warning\nresharper_is_expression_always_of_type_highlighting = warning\nresharper_is_expression_always_true_highlighting = warning\nresharper_iterator_method_result_is_ignored_highlighting = warning\nresharper_iterator_never_returns_highlighting = warning\nresharper_join_declaration_and_initializer_highlighting = suggestion\nresharper_join_null_check_with_usage_highlighting = suggestion\nresharper_join_null_check_with_usage_when_possible_highlighting = none\nresharper_lambda_expression_can_be_made_static_highlighting = none\nresharper_lambda_expression_must_be_static_highlighting = suggestion\nresharper_lambda_should_not_capture_context_highlighting = suggestion\nresharper_localizable_element_highlighting = warning\nresharper_local_function_can_be_made_static_highlighting = none\nresharper_local_function_hides_method_highlighting = warning\nresharper_local_variable_hides_member_highlighting = warning\nresharper_long_literal_ending_lower_l_highlighting = warning\nresharper_loop_can_be_converted_to_query_highlighting = hint\nresharper_loop_can_be_partly_converted_to_query_highlighting = none\nresharper_loop_variable_is_never_changed_inside_loop_highlighting = warning\nresharper_markup_attribute_typo_highlighting = hint\nresharper_markup_text_typo_highlighting = hint\nresharper_math_abs_method_is_redundant_highlighting = warning\nresharper_math_clamp_min_greater_than_max_highlighting = warning\nresharper_meaningless_default_parameter_value_highlighting = warning\nresharper_member_can_be_internal_highlighting = none\nresharper_member_can_be_made_static_global_highlighting = none\nresharper_member_can_be_made_static_local_highlighting = none\nresharper_member_can_be_private_global_highlighting = suggestion\nresharper_member_can_be_private_local_highlighting = suggestion\nresharper_member_can_be_protected_global_highlighting = suggestion\nresharper_member_can_be_protected_local_highlighting = suggestion\nresharper_member_hides_interface_member_with_default_implementation_highlighting = warning\nresharper_member_hides_static_from_outer_class_highlighting = warning\nresharper_member_initializer_value_ignored_highlighting = warning\nresharper_merge_and_pattern_highlighting = suggestion\nresharper_merge_cast_with_type_check_highlighting = suggestion\nresharper_merge_conditional_expression_highlighting = suggestion\nresharper_merge_conditional_expression_when_possible_highlighting = none\nresharper_merge_into_logical_pattern_highlighting = hint\nresharper_merge_into_negated_pattern_highlighting = hint\nresharper_merge_into_pattern_highlighting = suggestion\nresharper_merge_sequential_checks_highlighting = hint\nresharper_merge_sequential_checks_when_possible_highlighting = none\nresharper_method_has_async_overload_highlighting = suggestion\nresharper_method_has_async_overload_with_cancellation_highlighting = suggestion\nresharper_method_overload_with_optional_parameter_highlighting = warning\nresharper_method_supports_cancellation_highlighting = suggestion\nresharper_missing_alt_attribute_in_img_tag_highlighting = hint\nresharper_missing_blank_lines_highlighting = none\nresharper_missing_body_tag_highlighting = warning\nresharper_missing_head_and_body_tags_highlighting = warning\nresharper_missing_head_tag_highlighting = warning\nresharper_missing_indent_highlighting = none\nresharper_missing_linebreak_highlighting = none\nresharper_missing_space_highlighting = none\nresharper_missing_title_tag_highlighting = hint\nresharper_more_specific_foreach_variable_type_available_highlighting = suggestion\nresharper_move_to_existing_positional_deconstruction_pattern_highlighting = hint\nresharper_multiple_nullable_attributes_usage_highlighting = warning\nresharper_multiple_order_by_highlighting = warning\nresharper_multiple_resolve_candidates_in_text_highlighting = warning\nresharper_multiple_spaces_highlighting = none\nresharper_multiple_statements_on_one_line_highlighting = none\nresharper_multiple_type_members_on_one_line_highlighting = none\nresharper_must_use_return_value_highlighting = warning\nresharper_mvc_action_not_resolved_highlighting = error\nresharper_mvc_area_not_resolved_highlighting = error\nresharper_mvc_controller_not_resolved_highlighting = error\nresharper_mvc_invalid_model_type_highlighting = error\nresharper_mvc_masterpage_not_resolved_highlighting = error\nresharper_mvc_partial_view_not_resolved_highlighting = error\nresharper_mvc_template_not_resolved_highlighting = error\nresharper_mvc_view_component_not_resolved_highlighting = error\nresharper_mvc_view_component_view_not_resolved_highlighting = error\nresharper_mvc_view_not_resolved_highlighting = error\nresharper_negation_of_relational_pattern_highlighting = suggestion\nresharper_negative_equality_expression_highlighting = suggestion\nresharper_negative_index_highlighting = warning\nresharper_nested_string_interpolation_highlighting = suggestion\nresharper_non_atomic_compound_operator_highlighting = warning\nresharper_non_constant_equality_expression_has_constant_result_highlighting = warning\nresharper_non_parsable_element_highlighting = warning\nresharper_non_readonly_member_in_get_hash_code_highlighting = warning\nresharper_non_volatile_field_in_double_check_locking_highlighting = warning\nresharper_not_accessed_field_global_highlighting = suggestion\nresharper_not_accessed_field_local_highlighting = warning\nresharper_not_accessed_variable_highlighting = warning\nresharper_not_assigned_out_parameter_highlighting = warning\nresharper_not_declared_in_parent_culture_highlighting = warning\nresharper_not_null_member_is_not_initialized_highlighting = warning\nresharper_not_observable_annotation_redundancy_highlighting = warning\nresharper_not_overridden_in_specific_culture_highlighting = warning\nresharper_not_resolved_in_text_highlighting = warning\nresharper_no_support_for_vb_highlighting = warning\nresharper_nullable_warning_suppression_is_used_highlighting = none\nresharper_n_unit_async_method_must_be_task_highlighting = warning\nresharper_n_unit_attribute_produces_too_many_tests_highlighting = none\nresharper_n_unit_auto_fixture_incorrect_argument_type_highlighting = warning\nresharper_n_unit_auto_fixture_missed_test_attribute_highlighting = warning\nresharper_n_unit_auto_fixture_missed_test_or_test_fixture_attribute_highlighting = warning\nresharper_n_unit_auto_fixture_redundant_argument_in_inline_auto_data_attribute_highlighting = warning\nresharper_n_unit_duplicate_values_highlighting = warning\nresharper_n_unit_ignored_parameter_attribute_highlighting = warning\nresharper_n_unit_implicit_unspecified_null_values_highlighting = warning\nresharper_n_unit_incorrect_argument_type_highlighting = warning\nresharper_n_unit_incorrect_expected_result_type_highlighting = warning\nresharper_n_unit_incorrect_range_bounds_highlighting = warning\nresharper_n_unit_method_with_parameters_and_test_attribute_highlighting = warning\nresharper_n_unit_missing_arguments_in_test_case_attribute_highlighting = warning\nresharper_n_unit_non_public_method_with_test_attribute_highlighting = warning\nresharper_n_unit_no_values_provided_highlighting = warning\nresharper_n_unit_parameter_type_is_not_compatible_with_attribute_highlighting = warning\nresharper_n_unit_range_attribute_bounds_are_out_of_range_highlighting = warning\nresharper_n_unit_range_step_sign_mismatch_highlighting = warning\nresharper_n_unit_range_step_value_must_not_be_zero_highlighting = warning\nresharper_n_unit_range_to_value_is_not_reachable_highlighting = warning\nresharper_n_unit_redundant_argument_instead_of_expected_result_highlighting = warning\nresharper_n_unit_redundant_argument_in_test_case_attribute_highlighting = warning\nresharper_n_unit_redundant_expected_result_in_test_case_attribute_highlighting = warning\nresharper_n_unit_test_case_attribute_requires_expected_result_highlighting = warning\nresharper_n_unit_test_case_result_property_duplicates_expected_result_highlighting = warning\nresharper_n_unit_test_case_result_property_is_obsolete_highlighting = warning\nresharper_n_unit_test_case_source_cannot_be_resolved_highlighting = warning\nresharper_n_unit_test_case_source_must_be_field_property_method_highlighting = warning\nresharper_n_unit_test_case_source_must_be_static_highlighting = warning\nresharper_n_unit_test_case_source_should_implement_i_enumerable_highlighting = warning\nresharper_object_creation_as_statement_highlighting = none\nresharper_obsolete_element_error_highlighting = error\nresharper_obsolete_element_highlighting = warning\nresharper_ol_tag_contains_non_li_elements_highlighting = hint\nresharper_one_way_operation_contract_with_return_type_highlighting = warning\nresharper_operation_contract_without_service_contract_highlighting = warning\nresharper_operator_is_can_be_used_highlighting = warning\nresharper_optional_parameter_hierarchy_mismatch_highlighting = warning\nresharper_optional_parameter_ref_out_highlighting = warning\nresharper_other_tags_inside_script1_highlighting = error\nresharper_other_tags_inside_script2_highlighting = error\nresharper_other_tags_inside_unclosed_script_highlighting = error\nresharper_outdent_is_off_prev_level_highlighting = none\nresharper_overridden_with_empty_value_highlighting = warning\nresharper_overridden_with_same_value_highlighting = suggestion\nresharper_parameter_hides_member_highlighting = warning\nresharper_parameter_only_used_for_precondition_check_global_highlighting = suggestion\nresharper_parameter_only_used_for_precondition_check_local_highlighting = warning\nresharper_parameter_type_can_be_enumerable_global_highlighting = hint\nresharper_parameter_type_can_be_enumerable_local_highlighting = hint\nresharper_partial_method_parameter_name_mismatch_highlighting = warning\nresharper_partial_method_with_single_part_highlighting = warning\nresharper_partial_type_with_single_part_highlighting = warning\nresharper_pattern_always_matches_highlighting = warning\nresharper_pattern_always_of_type_highlighting = warning\nresharper_pattern_is_always_true_or_false_highlighting = warning\nresharper_pattern_never_matches_highlighting = warning\nresharper_polymorphic_field_like_event_invocation_highlighting = warning\nresharper_possible_infinite_inheritance_highlighting = warning\nresharper_possible_intended_rethrow_highlighting = warning\nresharper_possible_interface_member_ambiguity_highlighting = warning\nresharper_possible_invalid_cast_exception_highlighting = warning\nresharper_possible_invalid_cast_exception_in_foreach_loop_highlighting = warning\nresharper_possible_invalid_operation_exception_highlighting = hint\nresharper_possible_loss_of_fraction_highlighting = warning\nresharper_possible_mistaken_argument_highlighting = warning\nresharper_possible_mistaken_call_to_get_type_1_highlighting = warning\nresharper_possible_mistaken_call_to_get_type_2_highlighting = warning\nresharper_possible_multiple_enumeration_highlighting = suggestion\nresharper_possible_multiple_write_access_in_double_check_locking_highlighting = warning\nresharper_possible_null_reference_exception_highlighting = hint\nresharper_possible_struct_member_modification_of_non_variable_struct_highlighting = warning\nresharper_possible_unintended_linear_search_in_set_highlighting = warning\nresharper_possible_unintended_queryable_as_enumerable_highlighting = suggestion\nresharper_possible_unintended_reference_comparison_highlighting = warning\nresharper_possible_write_to_me_highlighting = warning\nresharper_possibly_impure_method_call_on_readonly_variable_highlighting = warning\nresharper_possibly_missing_indexer_initializer_comma_highlighting = warning\nresharper_possibly_mistaken_use_of_interpolated_string_insert_highlighting = warning\nresharper_possibly_mistaken_use_of_params_method_highlighting = hint\nresharper_private_field_can_be_converted_to_local_variable_highlighting = warning\nresharper_property_can_be_made_init_only_global_highlighting = suggestion\nresharper_property_can_be_made_init_only_local_highlighting = suggestion\nresharper_property_not_resolved_highlighting = error\nresharper_public_constructor_in_abstract_class_highlighting = suggestion\nresharper_pure_attribute_on_void_method_highlighting = warning\nresharper_razor_layout_not_resolved_highlighting = error\nresharper_razor_section_not_resolved_highlighting = error\nresharper_read_access_in_double_check_locking_highlighting = warning\nresharper_redundant_abstract_modifier_highlighting = warning\nresharper_redundant_always_match_subpattern_highlighting = suggestion\nresharper_redundant_anonymous_type_property_name_highlighting = warning\nresharper_redundant_argument_default_value_highlighting = none\nresharper_redundant_array_creation_expression_highlighting = hint\nresharper_redundant_array_lower_bound_specification_highlighting = warning\nresharper_redundant_assignment_highlighting = warning\nresharper_redundant_attribute_parentheses_highlighting = hint\nresharper_redundant_attribute_usage_property_highlighting = suggestion\nresharper_redundant_base_constructor_call_highlighting = warning\nresharper_redundant_base_qualifier_highlighting = warning\nresharper_redundant_blank_lines_highlighting = none\nresharper_redundant_bool_compare_highlighting = warning\nresharper_redundant_case_label_highlighting = warning\nresharper_redundant_cast_highlighting = warning\nresharper_redundant_catch_clause_highlighting = warning\nresharper_redundant_check_before_assignment_highlighting = warning\nresharper_redundant_collection_initializer_element_braces_highlighting = hint\nresharper_redundant_configure_await_highlighting = suggestion\nresharper_redundant_declaration_semicolon_highlighting = hint\nresharper_redundant_default_member_initializer_highlighting = warning\nresharper_redundant_delegate_creation_highlighting = warning\nresharper_redundant_disable_warning_comment_highlighting = warning\nresharper_redundant_discard_designation_highlighting = suggestion\nresharper_redundant_empty_case_else_highlighting = warning\nresharper_redundant_empty_finally_block_highlighting = warning\nresharper_redundant_empty_object_creation_argument_list_highlighting = none\nresharper_redundant_empty_object_or_collection_initializer_highlighting = warning\nresharper_redundant_empty_switch_section_highlighting = warning\nresharper_redundant_enumerable_cast_call_highlighting = warning\nresharper_redundant_explicit_array_creation_highlighting = warning\nresharper_redundant_explicit_array_size_highlighting = warning\nresharper_redundant_explicit_nullable_creation_highlighting = warning\nresharper_redundant_explicit_params_array_creation_highlighting = suggestion\nresharper_redundant_explicit_positional_property_declaration_highlighting = warning\nresharper_redundant_explicit_tuple_component_name_highlighting = warning\nresharper_redundant_extends_list_entry_highlighting = warning\nresharper_redundant_fixed_pointer_declaration_highlighting = suggestion\nresharper_redundant_if_else_block_highlighting = none\nresharper_redundant_if_statement_then_keyword_highlighting = none\nresharper_redundant_immediate_delegate_invocation_highlighting = suggestion\nresharper_redundant_include_highlighting = warning\nresharper_redundant_is_before_relational_pattern_highlighting = suggestion\nresharper_redundant_iterator_keyword_highlighting = warning\nresharper_redundant_jump_statement_highlighting = warning\nresharper_redundant_lambda_parameter_type_highlighting = warning\nresharper_redundant_lambda_signature_parentheses_highlighting = hint\nresharper_redundant_linebreak_highlighting = none\nresharper_redundant_logical_conditional_expression_operand_highlighting = warning\nresharper_redundant_me_qualifier_highlighting = warning\nresharper_redundant_my_base_qualifier_highlighting = warning\nresharper_redundant_my_class_qualifier_highlighting = warning\nresharper_redundant_name_qualifier_highlighting = warning\nresharper_redundant_not_null_constraint_highlighting = warning\nresharper_redundant_nullable_annotation_on_reference_type_constraint_highlighting = warning\nresharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_base_type_highlighting = warning\nresharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_type_kind_highlighting = warning\nresharper_redundant_nullable_flow_attribute_highlighting = warning\nresharper_redundant_nullable_type_mark_highlighting = warning\nresharper_redundant_nullness_attribute_with_nullable_reference_types_highlighting = warning\nresharper_redundant_overflow_checking_context_highlighting = warning\nresharper_redundant_overload_global_highlighting = suggestion\nresharper_redundant_overload_local_highlighting = suggestion\nresharper_redundant_overridden_member_highlighting = warning\nresharper_redundant_params_highlighting = warning\nresharper_redundant_parentheses_highlighting = none\nresharper_redundant_pattern_parentheses_highlighting = hint\nresharper_redundant_property_parentheses_highlighting = hint\nresharper_redundant_property_pattern_clause_highlighting = suggestion\nresharper_redundant_qualifier_highlighting = warning\nresharper_redundant_query_order_by_ascending_keyword_highlighting = hint\nresharper_redundant_range_bound_highlighting = suggestion\nresharper_redundant_readonly_modifier_highlighting = suggestion\nresharper_redundant_record_body_highlighting = warning\nresharper_redundant_record_class_keyword_highlighting = warning\nresharper_redundant_setter_value_parameter_declaration_highlighting = hint\nresharper_redundant_space_highlighting = none\nresharper_redundant_string_format_call_highlighting = warning\nresharper_redundant_string_interpolation_highlighting = suggestion\nresharper_redundant_string_to_char_array_call_highlighting = warning\nresharper_redundant_string_type_highlighting = suggestion\nresharper_redundant_suppress_nullable_warning_expression_highlighting = warning\nresharper_redundant_ternary_expression_highlighting = warning\nresharper_redundant_to_string_call_for_value_type_highlighting = hint\nresharper_redundant_to_string_call_highlighting = warning\nresharper_redundant_type_arguments_of_method_highlighting = warning\nresharper_redundant_unsafe_context_highlighting = warning\nresharper_redundant_using_directive_global_highlighting = warning\nresharper_redundant_using_directive_highlighting = warning\nresharper_redundant_verbatim_prefix_highlighting = suggestion\nresharper_redundant_verbatim_string_prefix_highlighting = suggestion\nresharper_redundant_with_expression_highlighting = suggestion\nresharper_reference_equals_with_value_type_highlighting = warning\nresharper_reg_exp_inspections_highlighting = warning\nresharper_remove_constructor_invocation_highlighting = none\nresharper_remove_redundant_braces_highlighting = hint\nresharper_remove_redundant_or_statement_false_highlighting = suggestion\nresharper_remove_redundant_or_statement_true_highlighting = suggestion\nresharper_remove_to_list_1_highlighting = suggestion\nresharper_remove_to_list_2_highlighting = suggestion\nresharper_replace_auto_property_with_computed_property_highlighting = hint\nresharper_replace_object_pattern_with_var_pattern_highlighting = suggestion\nresharper_replace_slice_with_range_indexer_highlighting = hint\nresharper_replace_substring_with_range_indexer_highlighting = hint\nresharper_replace_with_first_or_default_1_highlighting = suggestion\nresharper_replace_with_first_or_default_2_highlighting = suggestion\nresharper_replace_with_first_or_default_3_highlighting = suggestion\nresharper_replace_with_first_or_default_4_highlighting = suggestion\nresharper_replace_with_last_or_default_1_highlighting = suggestion\nresharper_replace_with_last_or_default_2_highlighting = suggestion\nresharper_replace_with_last_or_default_3_highlighting = suggestion\nresharper_replace_with_last_or_default_4_highlighting = suggestion\nresharper_replace_with_of_type_1_highlighting = suggestion\nresharper_replace_with_of_type_2_highlighting = suggestion\nresharper_replace_with_of_type_3_highlighting = suggestion\nresharper_replace_with_of_type_any_1_highlighting = suggestion\nresharper_replace_with_of_type_any_2_highlighting = suggestion\nresharper_replace_with_of_type_count_1_highlighting = suggestion\nresharper_replace_with_of_type_count_2_highlighting = suggestion\nresharper_replace_with_of_type_first_1_highlighting = suggestion\nresharper_replace_with_of_type_first_2_highlighting = suggestion\nresharper_replace_with_of_type_first_or_default_1_highlighting = suggestion\nresharper_replace_with_of_type_first_or_default_2_highlighting = suggestion\nresharper_replace_with_of_type_last_1_highlighting = suggestion\nresharper_replace_with_of_type_last_2_highlighting = suggestion\nresharper_replace_with_of_type_last_or_default_1_highlighting = suggestion\nresharper_replace_with_of_type_last_or_default_2_highlighting = suggestion\nresharper_replace_with_of_type_long_count_highlighting = suggestion\nresharper_replace_with_of_type_single_1_highlighting = suggestion\nresharper_replace_with_of_type_single_2_highlighting = suggestion\nresharper_replace_with_of_type_single_or_default_1_highlighting = suggestion\nresharper_replace_with_of_type_single_or_default_2_highlighting = suggestion\nresharper_replace_with_of_type_where_highlighting = suggestion\nresharper_replace_with_simple_assignment_false_highlighting = suggestion\nresharper_replace_with_simple_assignment_true_highlighting = suggestion\nresharper_replace_with_single_assignment_false_highlighting = suggestion\nresharper_replace_with_single_assignment_true_highlighting = suggestion\nresharper_replace_with_single_call_to_any_highlighting = suggestion\nresharper_replace_with_single_call_to_count_highlighting = suggestion\nresharper_replace_with_single_call_to_first_highlighting = suggestion\nresharper_replace_with_single_call_to_first_or_default_highlighting = suggestion\nresharper_replace_with_single_call_to_last_highlighting = suggestion\nresharper_replace_with_single_call_to_last_or_default_highlighting = suggestion\nresharper_replace_with_single_call_to_single_highlighting = suggestion\nresharper_replace_with_single_call_to_single_or_default_highlighting = suggestion\nresharper_replace_with_single_or_default_1_highlighting = suggestion\nresharper_replace_with_single_or_default_2_highlighting = suggestion\nresharper_replace_with_single_or_default_3_highlighting = suggestion\nresharper_replace_with_single_or_default_4_highlighting = suggestion\nresharper_replace_with_string_is_null_or_empty_highlighting = suggestion\nresharper_required_base_types_conflict_highlighting = warning\nresharper_required_base_types_direct_conflict_highlighting = warning\nresharper_required_base_types_is_not_inherited_highlighting = warning\nresharper_resource_item_not_resolved_highlighting = error\nresharper_resource_not_resolved_highlighting = error\nresharper_resx_not_resolved_highlighting = warning\nresharper_return_type_can_be_enumerable_global_highlighting = hint\nresharper_return_type_can_be_enumerable_local_highlighting = hint\nresharper_return_type_can_be_not_nullable_highlighting = warning\nresharper_return_value_of_pure_method_is_not_used_highlighting = warning\nresharper_route_templates_action_route_prefix_can_be_extracted_to_controller_route_highlighting = hint\nresharper_route_templates_ambiguous_matching_constraint_constructor_highlighting = warning\nresharper_route_templates_ambiguous_route_match_highlighting = warning\nresharper_route_templates_constraint_argument_cannot_be_converted_highlighting = warning\nresharper_route_templates_controller_route_parameter_is_not_passed_to_methods_highlighting = hint\nresharper_route_templates_duplicated_parameter_highlighting = warning\nresharper_route_templates_matching_constraint_constructor_not_resolved_highlighting = warning\nresharper_route_templates_method_missing_route_parameters_highlighting = hint\nresharper_route_templates_optional_parameter_can_be_preceded_only_by_single_period_highlighting = warning\nresharper_route_templates_optional_parameter_must_be_at_the_end_of_segment_highlighting = warning\nresharper_route_templates_parameter_constraint_can_be_specified_highlighting = hint\nresharper_route_templates_parameter_type_and_constraints_mismatch_highlighting = warning\nresharper_route_templates_parameter_type_can_be_made_stricter_highlighting = suggestion\nresharper_route_templates_route_parameter_constraint_not_resolved_highlighting = warning\nresharper_route_templates_route_parameter_is_not_passed_to_method_highlighting = hint\nresharper_route_templates_route_token_not_resolved_highlighting = warning\nresharper_route_templates_symbol_not_resolved_highlighting = warning\nresharper_route_templates_syntax_error_highlighting = warning\nresharper_safe_cast_is_used_as_type_check_highlighting = suggestion\nresharper_script_tag_has_both_src_and_content_attributes_highlighting = error\nresharper_script_tag_with_content_before_includes_highlighting = hint\nresharper_sealed_member_in_sealed_class_highlighting = warning\nresharper_separate_control_transfer_statement_highlighting = none\nresharper_service_contract_without_operations_highlighting = warning\nresharper_shift_expression_real_shift_count_is_zero_highlighting = warning\nresharper_shift_expression_result_equals_zero_highlighting = warning\nresharper_shift_expression_right_operand_not_equal_real_count_highlighting = warning\nresharper_shift_expression_zero_left_operand_highlighting = warning\nresharper_similar_anonymous_type_nearby_highlighting = hint\nresharper_simplify_conditional_operator_highlighting = suggestion\nresharper_simplify_conditional_ternary_expression_highlighting = suggestion\nresharper_simplify_i_if_highlighting = suggestion\nresharper_simplify_linq_expression_use_all_highlighting = suggestion\nresharper_simplify_linq_expression_use_any_highlighting = suggestion\nresharper_specify_a_culture_in_string_conversion_explicitly_highlighting = hint\nresharper_specify_string_comparison_highlighting = hint\nresharper_spin_lock_in_readonly_field_highlighting = warning\nresharper_stack_alloc_inside_loop_highlighting = warning\nresharper_static_member_initializer_referes_to_member_below_highlighting = warning\nresharper_static_member_in_generic_type_highlighting = warning\nresharper_static_problem_in_text_highlighting = warning\nresharper_string_compare_is_culture_specific_1_highlighting = warning\nresharper_string_compare_is_culture_specific_2_highlighting = warning\nresharper_string_compare_is_culture_specific_3_highlighting = warning\nresharper_string_compare_is_culture_specific_4_highlighting = warning\nresharper_string_compare_is_culture_specific_5_highlighting = warning\nresharper_string_compare_is_culture_specific_6_highlighting = warning\nresharper_string_compare_to_is_culture_specific_highlighting = warning\nresharper_string_ends_with_is_culture_specific_highlighting = none\nresharper_string_index_of_is_culture_specific_1_highlighting = warning\nresharper_string_index_of_is_culture_specific_2_highlighting = warning\nresharper_string_index_of_is_culture_specific_3_highlighting = warning\nresharper_string_last_index_of_is_culture_specific_1_highlighting = warning\nresharper_string_last_index_of_is_culture_specific_2_highlighting = warning\nresharper_string_last_index_of_is_culture_specific_3_highlighting = warning\nresharper_string_literal_as_interpolation_argument_highlighting = suggestion\nresharper_string_literal_typo_highlighting = hint\nresharper_string_starts_with_is_culture_specific_highlighting = none\nresharper_structured_message_template_problem_highlighting = warning\nresharper_struct_can_be_made_read_only_highlighting = suggestion\nresharper_struct_member_can_be_made_read_only_highlighting = none\nresharper_suggest_base_type_for_parameter_highlighting = hint\nresharper_suggest_base_type_for_parameter_in_constructor_highlighting = hint\nresharper_suggest_discard_declaration_var_style_highlighting = suggestion\nresharper_suggest_var_or_type_deconstruction_declarations_highlighting = hint\nresharper_suppress_nullable_warning_expression_as_inverted_is_expression_highlighting = warning\nresharper_suspicious_math_sign_method_highlighting = warning\nresharper_suspicious_parameter_name_in_argument_null_exception_highlighting = warning\nresharper_suspicious_type_conversion_global_highlighting = warning\nresharper_swap_via_deconstruction_highlighting = suggestion\nresharper_switch_expression_handles_some_known_enum_values_with_exception_in_default_highlighting = hint\nresharper_switch_statement_for_enum_misses_default_section_highlighting = hint\nresharper_switch_statement_handles_some_known_enum_values_with_default_highlighting = hint\nresharper_switch_statement_missing_some_enum_cases_no_default_highlighting = hint\nresharper_symbol_from_not_copied_locally_reference_used_warning_highlighting = warning\nresharper_tabs_and_spaces_mismatch_highlighting = none\nresharper_tabs_are_disallowed_highlighting = none\nresharper_tabs_outside_indent_highlighting = none\nresharper_tail_recursive_call_highlighting = hint\nresharper_thread_static_at_instance_field_highlighting = warning\nresharper_thread_static_field_has_initializer_highlighting = warning\nresharper_too_wide_local_variable_scope_highlighting = suggestion\nresharper_try_cast_always_succeeds_highlighting = suggestion\nresharper_try_statements_can_be_merged_highlighting = hint\nresharper_type_parameter_can_be_variant_highlighting = suggestion\nresharper_ul_tag_contains_non_li_elements_highlighting = hint\nresharper_unassigned_field_global_highlighting = suggestion\nresharper_unassigned_field_local_highlighting = warning\nresharper_unassigned_get_only_auto_property_highlighting = warning\nresharper_unassigned_readonly_field_highlighting = warning\nresharper_unclosed_script_highlighting = error\nresharper_unexpected_attribute_highlighting = warning\nresharper_unexpected_directive_highlighting = warning\nresharper_unnecessary_whitespace_highlighting = none\nresharper_unreachable_code_highlighting = warning\nresharper_unreachable_switch_arm_due_to_integer_analysis_highlighting = warning\nresharper_unreachable_switch_case_due_to_integer_analysis_highlighting = warning\nresharper_unreal_header_tool_error_highlighting = error\nresharper_unreal_header_tool_parser_error_highlighting = error\nresharper_unreal_header_tool_warning_highlighting = warning\nresharper_unsupported_required_base_type_highlighting = warning\nresharper_unused_anonymous_method_signature_highlighting = warning\nresharper_unused_auto_property_accessor_global_highlighting = suggestion\nresharper_unused_auto_property_accessor_local_highlighting = warning\nresharper_unused_import_clause_highlighting = warning\nresharper_unused_local_function_highlighting = warning\nresharper_unused_local_function_parameter_highlighting = warning\nresharper_unused_local_function_return_value_highlighting = warning\nresharper_unused_member_global_highlighting = suggestion\nresharper_unused_member_hierarchy_global_highlighting = suggestion\nresharper_unused_member_hierarchy_local_highlighting = warning\nresharper_unused_member_in_super_global_highlighting = suggestion\nresharper_unused_member_in_super_local_highlighting = warning\nresharper_unused_member_local_highlighting = warning\nresharper_unused_method_return_value_global_highlighting = suggestion\nresharper_unused_method_return_value_local_highlighting = warning\nresharper_unused_parameter_global_highlighting = suggestion\nresharper_unused_parameter_in_partial_method_highlighting = warning\nresharper_unused_parameter_local_highlighting = warning\nresharper_unused_tuple_component_in_return_value_highlighting = warning\nresharper_unused_type_global_highlighting = suggestion\nresharper_unused_type_local_highlighting = warning\nresharper_unused_type_parameter_highlighting = warning\nresharper_unused_variable_highlighting = warning\nresharper_useless_binary_operation_highlighting = warning\nresharper_useless_comparison_to_integral_constant_highlighting = warning\nresharper_use_array_creation_expression_1_highlighting = suggestion\nresharper_use_array_creation_expression_2_highlighting = suggestion\nresharper_use_array_empty_method_highlighting = suggestion\nresharper_use_await_using_highlighting = suggestion\nresharper_use_cancellation_token_for_i_async_enumerable_highlighting = suggestion\nresharper_use_collection_count_property_highlighting = warning\nresharper_use_configure_await_false_for_async_disposable_highlighting = none\nresharper_use_configure_await_false_highlighting = suggestion\nresharper_use_deconstruction_highlighting = hint\nresharper_use_deconstruction_on_parameter_highlighting = hint\nresharper_use_empty_types_field_highlighting = suggestion\nresharper_use_event_args_empty_field_highlighting = suggestion\nresharper_use_format_specifier_in_format_string_highlighting = suggestion\nresharper_use_format_specifier_in_interpolation_highlighting = suggestion\nresharper_use_implicitly_typed_variable_evident_highlighting = hint\nresharper_use_implicitly_typed_variable_highlighting = none\nresharper_use_implicit_by_val_modifier_highlighting = hint\nresharper_use_indexed_property_highlighting = suggestion\nresharper_use_index_from_end_expression_highlighting = suggestion\nresharper_use_is_operator_1_highlighting = suggestion\nresharper_use_is_operator_2_highlighting = suggestion\nresharper_use_method_any_0_highlighting = suggestion\nresharper_use_method_any_1_highlighting = suggestion\nresharper_use_method_any_2_highlighting = suggestion\nresharper_use_method_any_3_highlighting = suggestion\nresharper_use_method_any_4_highlighting = suggestion\nresharper_use_method_is_instance_of_type_highlighting = suggestion\nresharper_use_nameof_expression_for_part_of_the_string_highlighting = none\nresharper_use_nameof_expression_highlighting = suggestion\nresharper_use_name_of_instead_of_type_of_highlighting = suggestion\nresharper_use_negated_pattern_in_is_expression_highlighting = hint\nresharper_use_negated_pattern_matching_highlighting = hint\nresharper_use_nullable_annotation_instead_of_attribute_highlighting = suggestion\nresharper_use_nullable_attributes_supported_by_compiler_highlighting = suggestion\nresharper_use_nullable_reference_types_annotation_syntax_highlighting = warning\nresharper_use_null_propagation_highlighting = hint\nresharper_use_null_propagation_when_possible_highlighting = none\nresharper_use_object_or_collection_initializer_highlighting = suggestion\nresharper_use_pattern_matching_highlighting = suggestion\nresharper_use_positional_deconstruction_pattern_highlighting = none\nresharper_use_string_interpolation_highlighting = suggestion\nresharper_use_switch_case_pattern_variable_highlighting = suggestion\nresharper_use_verbatim_string_highlighting = hint\nresharper_value_parameter_not_used_highlighting = warning\nresharper_value_range_attribute_violation_highlighting = warning\nresharper_variable_can_be_not_nullable_highlighting = warning\nresharper_variable_hides_outer_variable_highlighting = warning\nresharper_vb_check_for_reference_equality_instead_1_highlighting = suggestion\nresharper_vb_check_for_reference_equality_instead_2_highlighting = suggestion\nresharper_vb_possible_mistaken_argument_highlighting = warning\nresharper_vb_possible_mistaken_call_to_get_type_1_highlighting = warning\nresharper_vb_possible_mistaken_call_to_get_type_2_highlighting = warning\nresharper_vb_remove_to_list_1_highlighting = suggestion\nresharper_vb_remove_to_list_2_highlighting = suggestion\nresharper_vb_replace_with_first_or_default_highlighting = suggestion\nresharper_vb_replace_with_last_or_default_highlighting = suggestion\nresharper_vb_replace_with_of_type_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_any_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_any_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_count_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_count_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_first_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_first_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_first_or_default_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_first_or_default_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_last_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_last_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_last_or_default_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_last_or_default_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_single_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_single_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_single_or_default_1_highlighting = suggestion\nresharper_vb_replace_with_of_type_single_or_default_2_highlighting = suggestion\nresharper_vb_replace_with_of_type_where_highlighting = suggestion\nresharper_vb_replace_with_single_assignment_1_highlighting = suggestion\nresharper_vb_replace_with_single_assignment_2_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_any_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_count_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_first_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_first_or_default_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_last_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_last_or_default_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_single_highlighting = suggestion\nresharper_vb_replace_with_single_call_to_single_or_default_highlighting = suggestion\nresharper_vb_replace_with_single_or_default_highlighting = suggestion\nresharper_vb_simplify_linq_expression_10_highlighting = hint\nresharper_vb_simplify_linq_expression_1_highlighting = suggestion\nresharper_vb_simplify_linq_expression_2_highlighting = suggestion\nresharper_vb_simplify_linq_expression_3_highlighting = suggestion\nresharper_vb_simplify_linq_expression_4_highlighting = suggestion\nresharper_vb_simplify_linq_expression_5_highlighting = suggestion\nresharper_vb_simplify_linq_expression_6_highlighting = suggestion\nresharper_vb_simplify_linq_expression_7_highlighting = hint\nresharper_vb_simplify_linq_expression_8_highlighting = hint\nresharper_vb_simplify_linq_expression_9_highlighting = hint\nresharper_vb_string_compare_is_culture_specific_1_highlighting = warning\nresharper_vb_string_compare_is_culture_specific_2_highlighting = warning\nresharper_vb_string_compare_is_culture_specific_3_highlighting = warning\nresharper_vb_string_compare_is_culture_specific_4_highlighting = warning\nresharper_vb_string_compare_is_culture_specific_5_highlighting = warning\nresharper_vb_string_compare_is_culture_specific_6_highlighting = warning\nresharper_vb_string_compare_to_is_culture_specific_highlighting = warning\nresharper_vb_string_ends_with_is_culture_specific_highlighting = none\nresharper_vb_string_index_of_is_culture_specific_1_highlighting = warning\nresharper_vb_string_index_of_is_culture_specific_2_highlighting = warning\nresharper_vb_string_index_of_is_culture_specific_3_highlighting = warning\nresharper_vb_string_last_index_of_is_culture_specific_1_highlighting = warning\nresharper_vb_string_last_index_of_is_culture_specific_2_highlighting = warning\nresharper_vb_string_last_index_of_is_culture_specific_3_highlighting = warning\nresharper_vb_string_starts_with_is_culture_specific_highlighting = none\nresharper_vb_unreachable_code_highlighting = warning\nresharper_vb_use_array_creation_expression_1_highlighting = suggestion\nresharper_vb_use_array_creation_expression_2_highlighting = suggestion\nresharper_vb_use_first_instead_highlighting = warning\nresharper_vb_use_method_any_1_highlighting = suggestion\nresharper_vb_use_method_any_2_highlighting = suggestion\nresharper_vb_use_method_any_3_highlighting = suggestion\nresharper_vb_use_method_any_4_highlighting = suggestion\nresharper_vb_use_method_any_5_highlighting = suggestion\nresharper_vb_use_method_is_instance_of_type_highlighting = suggestion\nresharper_vb_use_type_of_is_operator_1_highlighting = suggestion\nresharper_vb_use_type_of_is_operator_2_highlighting = suggestion\nresharper_virtual_member_call_in_constructor_highlighting = warning\nresharper_virtual_member_never_overridden_global_highlighting = suggestion\nresharper_virtual_member_never_overridden_local_highlighting = suggestion\nresharper_void_method_with_must_use_return_value_attribute_highlighting = warning\nresharper_web_config_module_not_resolved_highlighting = warning\nresharper_web_config_module_qualification_resolve_highlighting = warning\nresharper_web_config_redundant_add_namespace_tag_highlighting = warning\nresharper_web_config_redundant_location_tag_highlighting = warning\nresharper_web_config_tag_prefix_redundand_highlighting = warning\nresharper_web_config_type_not_resolved_highlighting = warning\nresharper_web_config_unused_add_tag_highlighting = warning\nresharper_web_config_unused_element_due_to_config_source_attribute_highlighting = warning\nresharper_web_config_unused_remove_or_clear_tag_highlighting = warning\nresharper_web_config_web_config_path_warning_highlighting = warning\nresharper_web_config_wrong_module_highlighting = warning\nresharper_web_ignored_path_highlighting = none\nresharper_web_mapped_path_highlighting = hint\nresharper_with_expression_instead_of_initializer_highlighting = suggestion\nresharper_wrong_indent_size_highlighting = none\nresharper_xaml_assign_null_to_not_null_attribute_highlighting = warning\nresharper_xaml_avalonia_wrong_binding_mode_for_stream_binding_operator_highlighting = warning\nresharper_xaml_binding_without_context_not_resolved_highlighting = hint\nresharper_xaml_binding_with_context_not_resolved_highlighting = warning\nresharper_xaml_compiled_binding_missing_data_type_error_highlighting_highlighting = error\nresharper_xaml_constructor_warning_highlighting = warning\nresharper_xaml_decimal_parsing_is_culture_dependent_highlighting = warning\nresharper_xaml_dependency_property_resolve_error_highlighting = warning\nresharper_xaml_duplicate_style_setter_highlighting = warning\nresharper_xaml_dynamic_resource_error_highlighting = error\nresharper_xaml_element_name_reference_not_resolved_highlighting = error\nresharper_xaml_empty_grid_length_definition_highlighting = error\nresharper_xaml_grid_definitions_can_be_converted_to_attribute_highlighting = hint\nresharper_xaml_ignored_path_highlighting_highlighting = none\nresharper_xaml_index_out_of_grid_definition_highlighting = warning\nresharper_xaml_invalid_member_type_highlighting = error\nresharper_xaml_invalid_resource_target_type_highlighting = error\nresharper_xaml_invalid_resource_type_highlighting = error\nresharper_xaml_invalid_type_highlighting = error\nresharper_xaml_language_level_highlighting = error\nresharper_xaml_mapped_path_highlighting_highlighting = hint\nresharper_xaml_method_arguments_will_be_ignored_highlighting = warning\nresharper_xaml_missing_grid_index_highlighting = warning\nresharper_xaml_overloads_collision_highlighting = warning\nresharper_xaml_parent_is_out_of_current_component_tree_highlighting = warning\nresharper_xaml_path_error_highlighting = warning\nresharper_xaml_possible_null_reference_exception_highlighting = suggestion\nresharper_xaml_redundant_attached_property_highlighting = warning\nresharper_xaml_redundant_binding_mode_attribute_highlighting = warning\nresharper_xaml_redundant_collection_property_highlighting = warning\nresharper_xaml_redundant_freeze_attribute_highlighting = warning\nresharper_xaml_redundant_grid_definitions_highlighting = warning\nresharper_xaml_redundant_grid_span_highlighting = warning\nresharper_xaml_redundant_modifiers_attribute_highlighting = warning\nresharper_xaml_redundant_namespace_alias_highlighting = warning\nresharper_xaml_redundant_name_attribute_highlighting = warning\nresharper_xaml_redundant_property_type_qualifier_highlighting = warning\nresharper_xaml_redundant_resource_highlighting = warning\nresharper_xaml_redundant_styled_value_highlighting = warning\nresharper_xaml_redundant_update_source_trigger_attribute_highlighting = warning\nresharper_xaml_redundant_xamarin_forms_class_declaration_highlighting = warning\nresharper_xaml_resource_file_path_case_mismatch_highlighting = warning\nresharper_xaml_routed_event_resolve_error_highlighting = warning\nresharper_xaml_static_resource_not_resolved_highlighting = warning\nresharper_xaml_style_class_not_found_highlighting = warning\nresharper_xaml_style_invalid_target_type_highlighting = error\nresharper_xaml_unexpected_text_token_highlighting = error\nresharper_xaml_xaml_duplicate_device_family_type_view_highlighting_highlighting = error\nresharper_xaml_xaml_mismatched_device_family_view_clr_name_highlighting_highlighting = warning\nresharper_xaml_xaml_relative_source_default_mode_warning_highlighting_highlighting = warning\nresharper_xaml_xaml_unknown_device_family_type_highlighting_highlighting = warning\nresharper_xaml_xaml_xamarin_forms_data_type_and_binding_context_type_mismatched_highlighting_highlighting = warning\nresharper_xaml_x_key_attribute_disallowed_highlighting = error\nresharper_xunit_xunit_test_with_console_output_highlighting = warning\n[{*.cql,*.ddl,*.sql}]\nindent_style = space\nindent_size = 4\n\n[.editorconfig]\nindent_style = space\nindent_size = 4\n\n[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]\nindent_style = space\nindent_size = 2\ntab_width = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Verification build\n\non:\n  workflow_dispatch:\n  pull_request:\n    branches: [\"master\"]\n    paths:\n      - \"src/**\"\n      - \"test/**\"\n\npermissions: write-all\n\njobs:\n  build-and-test:\n    runs-on: ubuntu-latest\n\n    services:\n      postgres:\n        image: postgres\n        env:\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_HOST_AUTH_METHOD: trust\n          TZ: UTC+13\n          PGTZ: UTC+13\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n        ports:\n          - 5432:5432\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: 9.0.x\n      - name: Restore dependencies\n        run: dotnet restore\n      - name: Build\n        run: dotnet build --no-restore\n      - name: Test\n        run: dotnet test --no-build --verbosity normal --logger \"trx;LogFileName=TestResults.trx\"\n      - name: Test Report\n        uses: actions/upload-artifact@v4\n        if: success() || failure()\n        with:\n          name: test-results\n          path: \"**/TestResults.trx\"\n"
  },
  {
    "path": ".github/workflows/pack.yml",
    "content": "name: Build and tests\n\non:\n  workflow_dispatch:\n  push:\n    branches: [\"master\"]\n    paths: [\"src/**\"]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: 9.0.x\n      - name: Restore dependencies\n        run: dotnet restore\n      - name: Build\n        run: dotnet build --no-restore -c Release\n      - name: Pack\n        run: dotnet pack -c Release --no-build --output publish\n      - name: Upload a Build Artifact\n        uses: actions/upload-artifact@v4\n        with:\n          name: Package\n          path: publish/**.nupkg\n\n  test:\n    permissions: write-all\n    runs-on: ubuntu-latest\n\n    services:\n      postgres:\n        image: postgres\n        env:\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_HOST_AUTH_METHOD: trust\n          TZ: UTC+13\n          PGTZ: UTC+13\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n        ports:\n          - 5432:5432\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: 9.0.x\n      - name: Restore dependencies\n        run: dotnet restore\n      - name: Build\n        run: dotnet build -c Release --no-restore\n      - name: Test\n        run: dotnet test -c Release --no-build --verbosity normal --logger \"trx;LogFileName=TestResults.trx\" || true\n      - name: Test Report\n        uses: dorny/test-reporter@v1\n        if: always()\n        with:\n          name: Test results\n          path: \"**/TestResults.trx\"\n          reporter: dotnet-trx\n          fail-on-error: true\n"
  },
  {
    "path": ".github/workflows/test-report.yml",
    "content": "name: Test report\n\non:\n  workflow_run:\n    workflows: ['Verification build', 'Build and tests']\n    types:\n      - completed\n\npermissions:\n  id-token: write\n  contents: read\n  checks: write\n\njobs:\n  report:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: dorny/test-reporter@v1\n      with:\n        artifact: test-results\n        name: Test results\n        path: \"**/TestResults.trx\"\n        reporter: dotnet-trx\n        fail-on-error: false\n"
  },
  {
    "path": ".gitignore",
    "content": "#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.classpath\n.settings/\n.loadpath\n\n# External tool builders\n.externalToolBuilders/\n\n# Locally stored \"Eclipse launch configurations\"\n*.launch\n\n# CDT-specific\n.cproject\n\n# PDT-specific\n.buildpath\n\n\n#################\n## Visual Studio\n#################\n\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\npackages/\n\n# Build results\n\n[Bb]uild/\n[Dd]ebug/\n[Rr]elease/\nx64/\n[Bb]in/\n[Oo]bj/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n*_i.c\n*_p.c\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.log\n*.scc\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n*.ncrunch*\n.*crunch*.local.xml\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.Publish.xml\n*.pubxml\n\n# NuGet Packages Directory\n## TODO: If you have NuGet Package Restore enabled, uncomment the next line\n#packages/\n\n# Windows Azure Build Output\ncsx\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\nsql/\n*.Cache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.[Pp]ublish.xml\n*.pfx\n*.publishsettings\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\nApp_Data/*.mdf\nApp_Data/*.ldf\n\n#############\n## Windows detritus\n#############\n\n# Windows image file caches\nThumbs.db\nehthumbs.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Mac crap\n.DS_Store\n\n\n#############\n## Python\n#############\n\n*.py[co]\n\n# Packages\n*.egg\n*.egg-info\ndist/\neggs/\nparts/\nvar/\nsdist/\ndevelop-eggs/\n.installed.cfg\n\n# Installer logs\npip-log.txt\n\n# Unit test / coverage reports\n.coverage\n.tox\n\n#Translations\n*.mo\n\n#Mr Developer\n.mr.developer.cfg\n\n#Sphinx Built Docs\ndocs/_build\n\n#Jekyll site builds\n_site\n*.userprefs\nsrc/Hangfire.PostgreSql/.vs/restore.dg\nsrc/Hangfire.PostgreSql/project.lock.json\n*/**/project.lock.json\ntools\n.vs\n.idea\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"sqltools.connections\": [\n        {\n            \"previewLimit\": 50,\n            \"server\": \"localhost\",\n            \"port\": 5432,\n            \"driver\": \"PostgreSQL\",\n            \"name\": \"localhost\",\n            \"database\": \"postgres\",\n            \"username\": \"postgres\",\n            \"password\": \"password\"\n        }\n    ]\n}"
  },
  {
    "path": "COPYING",
    "content": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\n  This version of the GNU Lesser General Public License incorporates\nthe terms and conditions of version 3 of the GNU General Public\nLicense, supplemented by the additional permissions listed below.\n\n  0. Additional Definitions.\n\n  As used herein, \"this License\" refers to version 3 of the GNU Lesser\nGeneral Public License, and the \"GNU GPL\" refers to version 3 of the GNU\nGeneral Public License.\n\n  \"The Library\" refers to a covered work governed by this License,\nother than an Application or a Combined Work as defined below.\n\n  An \"Application\" is any work that makes use of an interface provided\nby the Library, but which is not otherwise based on the Library.\nDefining a subclass of a class defined by the Library is deemed a mode\nof using an interface provided by the Library.\n\n  A \"Combined Work\" is a work produced by combining or linking an\nApplication with the Library.  The particular version of the Library\nwith which the Combined Work was made is also called the \"Linked\nVersion\".\n\n  The \"Minimal Corresponding Source\" for a Combined Work means the\nCorresponding Source for the Combined Work, excluding any source code\nfor portions of the Combined Work that, considered in isolation, are\nbased on the Application, and not on the Linked Version.\n\n  The \"Corresponding Application Code\" for a Combined Work means the\nobject code and/or source code for the Application, including any data\nand utility programs needed for reproducing the Combined Work from the\nApplication, but excluding the System Libraries of the Combined Work.\n\n  1. Exception to Section 3 of the GNU GPL.\n\n  You may convey a covered work under sections 3 and 4 of this License\nwithout being bound by section 3 of the GNU GPL.\n\n  2. Conveying Modified Versions.\n\n  If you modify a copy of the Library, and, in your modifications, a\nfacility refers to a function or data to be supplied by an Application\nthat uses the facility (other than as an argument passed when the\nfacility is invoked), then you may convey a copy of the modified\nversion:\n\n   a) under this License, provided that you make a good faith effort to\n   ensure that, in the event an Application does not supply the\n   function or data, the facility still operates, and performs\n   whatever part of its purpose remains meaningful, or\n\n   b) under the GNU GPL, with none of the additional permissions of\n   this License applicable to that copy.\n\n  3. Object Code Incorporating Material from Library Header Files.\n\n  The object code form of an Application may incorporate material from\na header file that is part of the Library.  You may convey such object\ncode under terms of your choice, provided that, if the incorporated\nmaterial is not limited to numerical parameters, data structure\nlayouts and accessors, or small macros, inline functions and templates\n(ten or fewer lines in length), you do both of the following:\n\n   a) Give prominent notice with each copy of the object code that the\n   Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the object code with a copy of the GNU GPL and this license\n   document.\n\n  4. Combined Works.\n\n  You may convey a Combined Work under terms of your choice that,\ntaken together, effectively do not restrict modification of the\nportions of the Library contained in the Combined Work and reverse\nengineering for debugging such modifications, if you also do each of\nthe following:\n\n   a) Give prominent notice with each copy of the Combined Work that\n   the Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the Combined Work with a copy of the GNU GPL and this license\n   document.\n\n   c) For a Combined Work that displays copyright notices during\n   execution, include the copyright notice for the Library among\n   these notices, as well as a reference directing the user to the\n   copies of the GNU GPL and this license document.\n\n   d) Do one of the following:\n\n       0) Convey the Minimal Corresponding Source under the terms of this\n       License, and the Corresponding Application Code in a form\n       suitable for, and under terms that permit, the user to\n       recombine or relink the Application with a modified version of\n       the Linked Version to produce a modified Combined Work, in the\n       manner specified by section 6 of the GNU GPL for conveying\n       Corresponding Source.\n\n       1) Use a suitable shared library mechanism for linking with the\n       Library.  A suitable mechanism is one that (a) uses at run time\n       a copy of the Library already present on the user's computer\n       system, and (b) will operate properly with a modified version\n       of the Library that is interface-compatible with the Linked\n       Version.\n\n   e) Provide Installation Information, but only if you would otherwise\n   be required to provide such information under section 6 of the\n   GNU GPL, and only to the extent that such information is\n   necessary to install and execute a modified version of the\n   Combined Work produced by recombining or relinking the\n   Application with a modified version of the Linked Version. (If\n   you use option 4d0, the Installation Information must accompany\n   the Minimal Corresponding Source and Corresponding Application\n   Code. If you use option 4d1, you must provide the Installation\n   Information in the manner specified by section 6 of the GNU GPL\n   for conveying Corresponding Source.)\n\n  5. Combined Libraries.\n\n  You may place library facilities that are a work based on the\nLibrary side by side in a single library together with other library\nfacilities that are not Applications and are not covered by this\nLicense, and convey such a combined library under terms of your\nchoice, if you do both of the following:\n\n   a) Accompany the combined library with a copy of the same work based\n   on the Library, uncombined with any other library facilities,\n   conveyed under the terms of this License.\n\n   b) Give prominent notice with the combined library that part of it\n   is a work based on the Library, and explaining where to find the\n   accompanying uncombined form of the same work.\n\n  6. Revised Versions of the GNU Lesser General Public License.\n\n  The Free Software Foundation may publish revised and/or new versions\nof the GNU Lesser General Public License from time to time. Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.\n\n  Each version is given a distinguishing version number. If the\nLibrary as you received it specifies that a certain numbered version\nof the GNU Lesser General Public License \"or any later version\"\napplies to it, you have the option of following the terms and\nconditions either of that published version or of any later version\npublished by the Free Software Foundation. If the Library as you\nreceived it does not specify a version number of the GNU Lesser\nGeneral Public License, you may choose any version of the GNU Lesser\nGeneral Public License ever published by the Free Software Foundation.\n\n  If the Library as you received it specifies that a proxy can decide\nwhether future versions of the GNU Lesser General Public License shall\napply, that proxy's public statement of acceptance of any version is\npermanent authorization for you to choose that version for the\nLibrary.\n"
  },
  {
    "path": "COPYING.LESSER",
    "content": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\n  This version of the GNU Lesser General Public License incorporates\nthe terms and conditions of version 3 of the GNU General Public\nLicense, supplemented by the additional permissions listed below.\n\n  0. Additional Definitions.\n\n  As used herein, \"this License\" refers to version 3 of the GNU Lesser\nGeneral Public License, and the \"GNU GPL\" refers to version 3 of the GNU\nGeneral Public License.\n\n  \"The Library\" refers to a covered work governed by this License,\nother than an Application or a Combined Work as defined below.\n\n  An \"Application\" is any work that makes use of an interface provided\nby the Library, but which is not otherwise based on the Library.\nDefining a subclass of a class defined by the Library is deemed a mode\nof using an interface provided by the Library.\n\n  A \"Combined Work\" is a work produced by combining or linking an\nApplication with the Library.  The particular version of the Library\nwith which the Combined Work was made is also called the \"Linked\nVersion\".\n\n  The \"Minimal Corresponding Source\" for a Combined Work means the\nCorresponding Source for the Combined Work, excluding any source code\nfor portions of the Combined Work that, considered in isolation, are\nbased on the Application, and not on the Linked Version.\n\n  The \"Corresponding Application Code\" for a Combined Work means the\nobject code and/or source code for the Application, including any data\nand utility programs needed for reproducing the Combined Work from the\nApplication, but excluding the System Libraries of the Combined Work.\n\n  1. Exception to Section 3 of the GNU GPL.\n\n  You may convey a covered work under sections 3 and 4 of this License\nwithout being bound by section 3 of the GNU GPL.\n\n  2. Conveying Modified Versions.\n\n  If you modify a copy of the Library, and, in your modifications, a\nfacility refers to a function or data to be supplied by an Application\nthat uses the facility (other than as an argument passed when the\nfacility is invoked), then you may convey a copy of the modified\nversion:\n\n   a) under this License, provided that you make a good faith effort to\n   ensure that, in the event an Application does not supply the\n   function or data, the facility still operates, and performs\n   whatever part of its purpose remains meaningful, or\n\n   b) under the GNU GPL, with none of the additional permissions of\n   this License applicable to that copy.\n\n  3. Object Code Incorporating Material from Library Header Files.\n\n  The object code form of an Application may incorporate material from\na header file that is part of the Library.  You may convey such object\ncode under terms of your choice, provided that, if the incorporated\nmaterial is not limited to numerical parameters, data structure\nlayouts and accessors, or small macros, inline functions and templates\n(ten or fewer lines in length), you do both of the following:\n\n   a) Give prominent notice with each copy of the object code that the\n   Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the object code with a copy of the GNU GPL and this license\n   document.\n\n  4. Combined Works.\n\n  You may convey a Combined Work under terms of your choice that,\ntaken together, effectively do not restrict modification of the\nportions of the Library contained in the Combined Work and reverse\nengineering for debugging such modifications, if you also do each of\nthe following:\n\n   a) Give prominent notice with each copy of the Combined Work that\n   the Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the Combined Work with a copy of the GNU GPL and this license\n   document.\n\n   c) For a Combined Work that displays copyright notices during\n   execution, include the copyright notice for the Library among\n   these notices, as well as a reference directing the user to the\n   copies of the GNU GPL and this license document.\n\n   d) Do one of the following:\n\n       0) Convey the Minimal Corresponding Source under the terms of this\n       License, and the Corresponding Application Code in a form\n       suitable for, and under terms that permit, the user to\n       recombine or relink the Application with a modified version of\n       the Linked Version to produce a modified Combined Work, in the\n       manner specified by section 6 of the GNU GPL for conveying\n       Corresponding Source.\n\n       1) Use a suitable shared library mechanism for linking with the\n       Library.  A suitable mechanism is one that (a) uses at run time\n       a copy of the Library already present on the user's computer\n       system, and (b) will operate properly with a modified version\n       of the Library that is interface-compatible with the Linked\n       Version.\n\n   e) Provide Installation Information, but only if you would otherwise\n   be required to provide such information under section 6 of the\n   GNU GPL, and only to the extent that such information is\n   necessary to install and execute a modified version of the\n   Combined Work produced by recombining or relinking the\n   Application with a modified version of the Linked Version. (If\n   you use option 4d0, the Installation Information must accompany\n   the Minimal Corresponding Source and Corresponding Application\n   Code. If you use option 4d1, you must provide the Installation\n   Information in the manner specified by section 6 of the GNU GPL\n   for conveying Corresponding Source.)\n\n  5. Combined Libraries.\n\n  You may place library facilities that are a work based on the\nLibrary side by side in a single library together with other library\nfacilities that are not Applications and are not covered by this\nLicense, and convey such a combined library under terms of your\nchoice, if you do both of the following:\n\n   a) Accompany the combined library with a copy of the same work based\n   on the Library, uncombined with any other library facilities,\n   conveyed under the terms of this License.\n\n   b) Give prominent notice with the combined library that part of it\n   is a work based on the Library, and explaining where to find the\n   accompanying uncombined form of the same work.\n\n  6. Revised Versions of the GNU Lesser General Public License.\n\n  The Free Software Foundation may publish revised and/or new versions\nof the GNU Lesser General Public License from time to time. Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.\n\n  Each version is given a distinguishing version number. If the\nLibrary as you received it specifies that a certain numbered version\nof the GNU Lesser General Public License \"or any later version\"\napplies to it, you have the option of following the terms and\nconditions either of that published version or of any later version\npublished by the Free Software Foundation. If the Library as you\nreceived it does not specify a version number of the GNU Lesser\nGeneral Public License, you may choose any version of the GNU Lesser\nGeneral Public License ever published by the Free Software Foundation.\n\n  If the Library as you received it specifies that a proxy can decide\nwhether future versions of the GNU Lesser General Public License shall\napply, that proxy's public statement of acceptance of any version is\npermanent authorization for you to choose that version for the\nLibrary.\n"
  },
  {
    "path": "Hangfire.PostgreSql.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.5.33424.131\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"tests\", \"tests\", \"{766BE831-F758-46BC-AFD3-BBEEFE0F686F}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{5CA38188-92EE-453C-A04E-A506DF15A787}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tREADME.md = README.md\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"src\", \"src\", \"{0D30A51B-814F-474E-93B8-44E9C155255C}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.PostgreSql\", \"src\\Hangfire.PostgreSql\\Hangfire.PostgreSql.csproj\", \"{3E4DBC41-F38E-4D1C-A6A7-206A132A29D6}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.PostgreSql.Tests\", \"tests\\Hangfire.PostgreSql.Tests\\Hangfire.PostgreSql.Tests.csproj\", \"{6044A48D-730B-4D1F-B03A-EB2B458DAF53}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"workflows\", \"workflows\", \"{AAA78654-9846-4870-A13C-D9DBAF0792C4}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.github\\workflows\\ci.yml = .github\\workflows\\ci.yml\n\t\t.github\\workflows\\pack.yml = .github\\workflows\\pack.yml\n\tEndProjectSection\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{3E4DBC41-F38E-4D1C-A6A7-206A132A29D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{3E4DBC41-F38E-4D1C-A6A7-206A132A29D6}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{3E4DBC41-F38E-4D1C-A6A7-206A132A29D6}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{3E4DBC41-F38E-4D1C-A6A7-206A132A29D6}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6044A48D-730B-4D1F-B03A-EB2B458DAF53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6044A48D-730B-4D1F-B03A-EB2B458DAF53}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6044A48D-730B-4D1F-B03A-EB2B458DAF53}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6044A48D-730B-4D1F-B03A-EB2B458DAF53}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{3E4DBC41-F38E-4D1C-A6A7-206A132A29D6} = {0D30A51B-814F-474E-93B8-44E9C155255C}\n\t\t{6044A48D-730B-4D1F-B03A-EB2B458DAF53} = {766BE831-F758-46BC-AFD3-BBEEFE0F686F}\n\t\t{AAA78654-9846-4870-A13C-D9DBAF0792C4} = {5CA38188-92EE-453C-A04E-A506DF15A787}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {F7E32105-7F61-4127-8517-5E4275B9CABE}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Hangfire.PostgreSql.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Hangfire_002EAnnotations/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Hangfire_002ERedis_002EAnnotations/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Hangfire_002ESqlServer_002EAnnotations/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/Filtering/ExcludeCoverageFilters/=Hangfire_002ECore_002ETests_003B_002A_003B_002A_003B_002A/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/Filtering/ExcludeCoverageFilters/=Hangfire_002ESqlServer_002ETests_003B_002A_003B_002A_003B_002A/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EdotCover_002EIde_002ECore_002EFilterManagement_002EModel_002ESolutionFilterSettingsManagerMigrateSettings/@EntryIndexedValue\">True</s:Boolean>\n\t<s:String x:Key=\"/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue\">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters&gt;&lt;Filter ModuleMask=\"Hangfire.Core.Tests\" ModuleVersionMask=\"*\" ClassMask=\"*\" FunctionMask=\"*\" IsEnabled=\"True\" /&gt;&lt;Filter ModuleMask=\"Hangfire.SqlServer.Tests\" ModuleVersionMask=\"*\" ClassMask=\"*\" FunctionMask=\"*\" IsEnabled=\"True\" /&gt;&lt;/ExcludeFilters&gt;&lt;/data&gt;</s:String>\n\t<s:String x:Key=\"/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue\">&lt;data /&gt;</s:String></wpf:ResourceDictionary>"
  },
  {
    "path": "LICENSE.md",
    "content": "License\n========\n\nCopyright © 2014-2022 Frank Hommers https://github.com/frankhommers/Hangfire.PostgreSql and others (Burhan Irmikci (barhun), Zachary Sims(\nzsims), kgamecarter, Stafford Williams (staff0rd), briangweber, Viktor Svyatokha (ahydrax), Christopher Dresel (Dresel),\nVytautas Kasparavičius (vytautask), Vincent Vrijburg, David Roth (davidroth).\n\nHangfire.PostgreSql is an Open Source project licensed under the terms of the LGPLv3 license. Please\nsee http://www.gnu.org/licenses/lgpl-3.0.html for license text or COPYING.LESSER file distributed with the source code.\n\nThis work is based on the work of Sergey Odinokov, author of Hangfire. <http://hangfire.io/>\n"
  },
  {
    "path": "README.md",
    "content": "# Hangfire.PostgreSql\n\n[![Build status](https://github.com/hangfire-postgres/Hangfire.PostgreSql/actions/workflows/pack.yml/badge.svg)](https://github.com/hangfire-postgres/Hangfire.PostgreSql/actions/workflows/pack.yml) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/hangfire-postgres/Hangfire.PostgreSql?label=Release)](https://github.com/hangfire-postgres/Hangfire.PostgreSql/releases/latest) [![Nuget](https://img.shields.io/nuget/v/Hangfire.PostgreSql?label=NuGet)](https://www.nuget.org/packages/Hangfire.PostgreSql)\n\nThis is an plugin to the Hangfire to enable PostgreSQL as a storage system.\nRead about hangfire here: https://github.com/HangfireIO/Hangfire#overview\nand here: http://hangfire.io/\n\n## Instructions\n\n### For .NET\n\nInstall Hangfire, see https://github.com/HangfireIO/Hangfire#installation\n\nDownload all files from this repository, add the Hangfire.PostgreSql.csproj to your solution.\nReference it in your project, and you are ready to go by using:\n\n```csharp\napp.UseHangfireServer(new BackgroundJobServerOptions(),\n  new PostgreSqlStorage(\"<connection string or its name>\"));\napp.UseHangfireDashboard();\n```\n\n### For ASP.NET Core\n\nFirst, NuGet package needs installation.\n\n- Hangfire.AspNetCore\n- Hangfire.PostgreSql (Uses Npgsql 6)\n- Hangfire.PostgreSql.Npgsql5 (Uses Npgsql 5)\n\nHistorically both packages were functionally the same up until the package version 1.9.11, the only difference was the underlying Npgsql dependency version. Afterwards, the support for Npgsql v5 has been dropped and now minimum required version is 6.0.0.\n\nIn `Startup.cs` _ConfigureServices(IServiceCollection services)_ method add the following line:\n\n```csharp\nservices.AddHangfire(config =>\n    config.UsePostgreSqlStorage(c =>\n        c.UseNpgsqlConnection(Configuration.GetConnectionString(\"HangfireConnection\"))));\n```\n\nIn Configure method, add these two lines:\n\n```csharp\napp.UseHangfireServer();\napp.UseHangfireDashboard();\n```\n\nAnd... That's it. You are ready to go.\n\nIf you encounter any issues/bugs or have idea of a feature regarding Hangfire.Postgresql, [create us an issue](https://github.com/hangfire-postgres/Hangfire.PostgreSql/issues/new). Thanks!\n\n### Enabling SSL support\n\nSSL support can be enabled for Hangfire.PostgreSql library using the following mechanism:\n\n```csharp\nconfig.UsePostgreSqlStorage(c =>\n    c.UseNpgsqlConnection(\n        Configuration.GetConnectionString(\"HangfireConnection\"), // connection string,\n        connection => // connection setup - gets called after instantiating the connection and before any calls to DB are made\n        {\n            connection.ProvideClientCertificatesCallback += clientCerts =>\n            {\n                clientCerts.Add(X509Certificate.CreateFromCertFile(\"[CERT_FILENAME]\"));\n            };\n        }\n    )\n);\n```\n### Queue processing\n\nSimilar to `Hangfire.SqlServer`, queues are processed in alphabetical order. Given the following example\n\n```csharp\nvar options = new BackgroundJobServerOptions\n{\n    Queues = new[] { \"general-queue\", \"very-fast-queue\", \"a-long-running-queue\" }\n};\napp.UseHangfireServer(options);\n```\n\nthis provider would first process jobs in `a-long-running-queue`, then `general-queue` and lastly `very-fast-queue`.\n\n### Startup resilience and transient database outages\n\nStarting from version 1.20.13 (where `PostgreSqlStorageOptions` gained startup resilience options), the storage tries to be more tolerant to *transient* PostgreSQL outages during application startup.\n\n#### Default behavior\n\nBy default, when you use the new-style configuration:\n\n```csharp\nservices.AddHangfire((provider, config) =>\n{\n    config.UsePostgreSqlStorage(opts =>\n        opts.UseNpgsqlConnection(Configuration.GetConnectionString(\"HangfireConnection\")));\n});\n\napp.UseHangfireServer();\napp.UseHangfireDashboard();\n```\n\n`PostgreSqlStorageOptions` uses the following defaults for startup resilience:\n\n- `PrepareSchemaIfNecessary = true`\n- `StartupConnectionMaxRetries = 5`\n- `StartupConnectionBaseDelay = 1 second`\n- `StartupConnectionMaxDelay = 1 minute`\n- `AllowDegradedModeWithoutStorage = true`\n\nWith these defaults:\n\n1. On application startup, when schema preparation is required, the storage will try to open a connection and install/upgrade the schema.\n2. If the database is temporarily unavailable, it will retry the operation up to 6 times (1 initial attempt + 5 retries) with exponential backoff between attempts, capped at 1 minute.\n3. If all attempts fail **during startup**, the storage enters a *degraded* state instead of crashing the whole process. Your ASP.NET Core application can still start and serve other endpoints that do not depend on Hangfire.\n4. On the *first actual use* of the storage (e.g. dashboard, background job server), Hangfire will try to initialize again. If the database is available by then, initialization succeeds and everything works as usual. If it is still unavailable, an `InvalidOperationException` with the original database exception as `InnerException` is thrown at that call site.\n\nThis behavior is designed to make applications more robust in scenarios where the database may briefly lag behind the application during deployments or orchestrated restarts.\n\n#### Opting out of resilient startup (fail fast)\n\nIf you prefer to fail the whole process immediately if the database is not reachable during startup – you can disable retries by setting `StartupConnectionMaxRetries` to `0`:\n\n```csharp\nvar storageOptions = new PostgreSqlStorageOptions\n{\n    PrepareSchemaIfNecessary = true,\n    StartupConnectionMaxRetries = 0,          // disables resilient startup\n    AllowDegradedModeWithoutStorage = false,  // fail fast if DB is down at startup\n};\n\nservices.AddHangfire((provider, config) =>\n{\n    config.UsePostgreSqlStorage(opts =>\n        opts.UseNpgsqlConnection(Configuration.GetConnectionString(\"HangfireConnection\")),\n        storageOptions);\n});\n```\n\nWith this configuration:\n\n- A single attempt is made to open a connection and prepare the schema.\n- If that attempt fails, the storage constructor throws and application startup fails.\n\n#### Controlling degraded mode\n\nDegraded mode is controlled via `AllowDegradedModeWithoutStorage`:\n\n- `true` (default): if all startup attempts fail, both startup and lazy initialization will keep the storage in an uninitialized state on failure and retry on subsequent uses, until initialization eventually succeeds.\n- `false`: if all startup attempts fail, the storage constructor will throw an `InvalidOperationException(\"Failed to initialize Hangfire PostgreSQL storage.\", innerException)`.\n\nFor example, to keep retries but still fail startup if the DB never becomes available:\n\n```csharp\nvar storageOptions = new PostgreSqlStorageOptions\n{\n    PrepareSchemaIfNecessary = true,\n    StartupConnectionMaxRetries = 10,         // more aggressive retry policy\n    StartupConnectionBaseDelay = TimeSpan.FromSeconds(2),\n    StartupConnectionMaxDelay = TimeSpan.FromMinutes(2),\n    AllowDegradedModeWithoutStorage = false,  // do not start the app without storage\n};\n```\n\n#### Turning off schema preparation entirely\n\nIf you manage the Hangfire schema yourself (for example via migrations or a dedicated deployment step) and do not want the storage to touch the database during startup or on first use, set `PrepareSchemaIfNecessary = false`:\n\n```csharp\nvar storageOptions = new PostgreSqlStorageOptions\n{\n    PrepareSchemaIfNecessary = false, // no schema installation/upgrade\n};\n```\n\nIn this case:\n\n- No schema initialization is performed by `PostgreSqlStorage`.\n- The first query that actually needs the database will fail if the schema is missing or mismatched, so you must ensure it is created/updated out of band.\n\n> Note: startup resilience settings (`StartupConnectionMaxRetries`, `AllowDegradedModeWithoutStorage`, etc.) only apply when `PrepareSchemaIfNecessary` is `true`.\n\n### License\n\nCopyright © 2014-2024 Frank Hommers https://github.com/hangfire-postgres/Hangfire.PostgreSql.\n\nCollaborators:\nFrank Hommers (frankhommers), Vytautas Kasparavičius (vytautask), Žygimantas Arūna (azygis) \n\nContributors:\nAndrew Armstrong (Plasma), Burhan Irmikci (barhun), Zachary Sims(zsims), kgamecarter, Stafford Williams (staff0rd), briangweber, Viktor Svyatokha (ahydrax), Christopher Dresel (Dresel), Vincent Vrijburg, David Roth (davidroth) and Ivan Tiniakov (Tinyakov).\n\nHangfire.PostgreSql is an Open Source project licensed under the terms of the LGPLv3 license. Please see http://www.gnu.org/licenses/lgpl-3.0.html for license text or COPYING.LESSER file distributed with the source code.\n\nThis work is based on the work of Sergey Odinokov, author of Hangfire. <http://hangfire.io/>\n\n### Related Projects\n\n- [Hangfire.Core](https://github.com/HangfireIO/Hangfire)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nUse this section to tell people about which versions of your project are\ncurrently being supported with security updates.\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 1.6.3   | :white_check_mark: |\n| < 1.6.3 | :x:                |\n\n\n## Reporting a Vulnerability\n\nDo not create a new issue ticket with the detailed information of the vulnerability under any circumstances!\n\nAll vulnerabilities can be reported directly to the main project maintainers (currently @frankfommers and @vytautask) directly\n via email (frank-hfpg[at]hommers[dot]nl or vytautaskasp[at]gmail[dot]com respectively). We should get back to you in 24 hours. Please include as much information as possible (if you have a POC - even better). All security issues have our highest attention and will be solved ASAP. \n \nYou would help us out if you also include a fix in a pull request. That way we can quickly release a fix.\n\nThank you for your understanding.\n"
  },
  {
    "path": "src/Common/Hangfire.ruleset",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RuleSet Name=\"Hangfire rules\" Description=\"This rule set contains all rules. Running this rule set may result in a large number of warnings being reported. Use this rule set to get a comprehensive picture of all issues in your code. This can help you decide which of the more focused rule sets are most appropriate to run for your projects.\" ToolsVersion=\"11.0\">\n  <IncludeAll Action=\"Warning\" />\n  <Rules AnalyzerId=\"Microsoft.Analyzers.ManagedCodeAnalysis\" RuleNamespace=\"Microsoft.Rules.Managed\">\n    <Rule Id=\"CA1020\" Action=\"None\" />\n    <Rule Id=\"CA1063\" Action=\"None\" />\n    <Rule Id=\"CA1305\" Action=\"None\" />\n    <Rule Id=\"CA1704\" Action=\"None\" />\n    <Rule Id=\"CA1816\" Action=\"None\" />\n    <Rule Id=\"CA2243\" Action=\"None\" />\n  </Rules>\n  <Rules AnalyzerId=\"Microsoft.Analyzers.NativeCodeAnalysis\" RuleNamespace=\"Microsoft.Rules.Native\">\n    <Rule Id=\"C26100\" Action=\"None\" />\n    <Rule Id=\"C26101\" Action=\"None\" />\n    <Rule Id=\"C26105\" Action=\"None\" />\n    <Rule Id=\"C26110\" Action=\"None\" />\n    <Rule Id=\"C26111\" Action=\"None\" />\n    <Rule Id=\"C26112\" Action=\"None\" />\n    <Rule Id=\"C26115\" Action=\"None\" />\n    <Rule Id=\"C26116\" Action=\"None\" />\n    <Rule Id=\"C26117\" Action=\"None\" />\n    <Rule Id=\"C26130\" Action=\"None\" />\n    <Rule Id=\"C26135\" Action=\"None\" />\n    <Rule Id=\"C26140\" Action=\"None\" />\n    <Rule Id=\"C26160\" Action=\"None\" />\n    <Rule Id=\"C26165\" Action=\"None\" />\n    <Rule Id=\"C26166\" Action=\"None\" />\n    <Rule Id=\"C26167\" Action=\"None\" />\n    <Rule Id=\"C28020\" Action=\"None\" />\n    <Rule Id=\"C28021\" Action=\"None\" />\n    <Rule Id=\"C28022\" Action=\"None\" />\n    <Rule Id=\"C28023\" Action=\"None\" />\n    <Rule Id=\"C28024\" Action=\"None\" />\n    <Rule Id=\"C28039\" Action=\"None\" />\n    <Rule Id=\"C28101\" Action=\"None\" />\n    <Rule Id=\"C28103\" Action=\"None\" />\n    <Rule Id=\"C28104\" Action=\"None\" />\n    <Rule Id=\"C28105\" Action=\"None\" />\n    <Rule Id=\"C28106\" Action=\"None\" />\n    <Rule Id=\"C28107\" Action=\"None\" />\n    <Rule Id=\"C28108\" Action=\"None\" />\n    <Rule Id=\"C28109\" Action=\"None\" />\n    <Rule Id=\"C28110\" Action=\"None\" />\n    <Rule Id=\"C28111\" Action=\"None\" />\n    <Rule Id=\"C28112\" Action=\"None\" />\n    <Rule Id=\"C28113\" Action=\"None\" />\n    <Rule Id=\"C28114\" Action=\"None\" />\n    <Rule Id=\"C28120\" Action=\"None\" />\n    <Rule Id=\"C28121\" Action=\"None\" />\n    <Rule Id=\"C28122\" Action=\"None\" />\n    <Rule Id=\"C28123\" Action=\"None\" />\n    <Rule Id=\"C28124\" Action=\"None\" />\n    <Rule Id=\"C28125\" Action=\"None\" />\n    <Rule Id=\"C28126\" Action=\"None\" />\n    <Rule Id=\"C28127\" Action=\"None\" />\n    <Rule Id=\"C28128\" Action=\"None\" />\n    <Rule Id=\"C28129\" Action=\"None\" />\n    <Rule Id=\"C28131\" Action=\"None\" />\n    <Rule Id=\"C28132\" Action=\"None\" />\n    <Rule Id=\"C28133\" Action=\"None\" />\n    <Rule Id=\"C28134\" Action=\"None\" />\n    <Rule Id=\"C28135\" Action=\"None\" />\n    <Rule Id=\"C28137\" Action=\"None\" />\n    <Rule Id=\"C28138\" Action=\"None\" />\n    <Rule Id=\"C28139\" Action=\"None\" />\n    <Rule Id=\"C28141\" Action=\"None\" />\n    <Rule Id=\"C28143\" Action=\"None\" />\n    <Rule Id=\"C28144\" Action=\"None\" />\n    <Rule Id=\"C28145\" Action=\"None\" />\n    <Rule Id=\"C28146\" Action=\"None\" />\n    <Rule Id=\"C28147\" Action=\"None\" />\n    <Rule Id=\"C28150\" Action=\"None\" />\n    <Rule Id=\"C28151\" Action=\"None\" />\n    <Rule Id=\"C28152\" Action=\"None\" />\n    <Rule Id=\"C28153\" Action=\"None\" />\n    <Rule Id=\"C28156\" Action=\"None\" />\n    <Rule Id=\"C28157\" Action=\"None\" />\n    <Rule Id=\"C28158\" Action=\"None\" />\n    <Rule Id=\"C28159\" Action=\"None\" />\n    <Rule Id=\"C28160\" Action=\"None\" />\n    <Rule Id=\"C28161\" Action=\"None\" />\n    <Rule Id=\"C28162\" Action=\"None\" />\n    <Rule Id=\"C28163\" Action=\"None\" />\n    <Rule Id=\"C28164\" Action=\"None\" />\n    <Rule Id=\"C28165\" Action=\"None\" />\n    <Rule Id=\"C28166\" Action=\"None\" />\n    <Rule Id=\"C28167\" Action=\"None\" />\n    <Rule Id=\"C28168\" Action=\"None\" />\n    <Rule Id=\"C28169\" Action=\"None\" />\n    <Rule Id=\"C28170\" Action=\"None\" />\n    <Rule Id=\"C28171\" Action=\"None\" />\n    <Rule Id=\"C28172\" Action=\"None\" />\n    <Rule Id=\"C28173\" Action=\"None\" />\n    <Rule Id=\"C28175\" Action=\"None\" />\n    <Rule Id=\"C28176\" Action=\"None\" />\n    <Rule Id=\"C28177\" Action=\"None\" />\n    <Rule Id=\"C28182\" Action=\"None\" />\n    <Rule Id=\"C28183\" Action=\"None\" />\n    <Rule Id=\"C28193\" Action=\"None\" />\n    <Rule Id=\"C28194\" Action=\"None\" />\n    <Rule Id=\"C28195\" Action=\"None\" />\n    <Rule Id=\"C28196\" Action=\"None\" />\n    <Rule Id=\"C28197\" Action=\"None\" />\n    <Rule Id=\"C28198\" Action=\"None\" />\n    <Rule Id=\"C28199\" Action=\"None\" />\n    <Rule Id=\"C28202\" Action=\"None\" />\n    <Rule Id=\"C28203\" Action=\"None\" />\n    <Rule Id=\"C28204\" Action=\"None\" />\n    <Rule Id=\"C28205\" Action=\"None\" />\n    <Rule Id=\"C28206\" Action=\"None\" />\n    <Rule Id=\"C28207\" Action=\"None\" />\n    <Rule Id=\"C28208\" Action=\"None\" />\n    <Rule Id=\"C28209\" Action=\"None\" />\n    <Rule Id=\"C28210\" Action=\"None\" />\n    <Rule Id=\"C28211\" Action=\"None\" />\n    <Rule Id=\"C28212\" Action=\"None\" />\n    <Rule Id=\"C28213\" Action=\"None\" />\n    <Rule Id=\"C28214\" Action=\"None\" />\n    <Rule Id=\"C28215\" Action=\"None\" />\n    <Rule Id=\"C28216\" Action=\"None\" />\n    <Rule Id=\"C28217\" Action=\"None\" />\n    <Rule Id=\"C28218\" Action=\"None\" />\n    <Rule Id=\"C28219\" Action=\"None\" />\n    <Rule Id=\"C28220\" Action=\"None\" />\n    <Rule Id=\"C28221\" Action=\"None\" />\n    <Rule Id=\"C28222\" Action=\"None\" />\n    <Rule Id=\"C28223\" Action=\"None\" />\n    <Rule Id=\"C28224\" Action=\"None\" />\n    <Rule Id=\"C28225\" Action=\"None\" />\n    <Rule Id=\"C28226\" Action=\"None\" />\n    <Rule Id=\"C28227\" Action=\"None\" />\n    <Rule Id=\"C28228\" Action=\"None\" />\n    <Rule Id=\"C28229\" Action=\"None\" />\n    <Rule Id=\"C28230\" Action=\"None\" />\n    <Rule Id=\"C28231\" Action=\"None\" />\n    <Rule Id=\"C28232\" Action=\"None\" />\n    <Rule Id=\"C28233\" Action=\"None\" />\n    <Rule Id=\"C28234\" Action=\"None\" />\n    <Rule Id=\"C28235\" Action=\"None\" />\n    <Rule Id=\"C28236\" Action=\"None\" />\n    <Rule Id=\"C28237\" Action=\"None\" />\n    <Rule Id=\"C28238\" Action=\"None\" />\n    <Rule Id=\"C28239\" Action=\"None\" />\n    <Rule Id=\"C28240\" Action=\"None\" />\n    <Rule Id=\"C28241\" Action=\"None\" />\n    <Rule Id=\"C28243\" Action=\"None\" />\n    <Rule Id=\"C28244\" Action=\"None\" />\n    <Rule Id=\"C28245\" Action=\"None\" />\n    <Rule Id=\"C28246\" Action=\"None\" />\n    <Rule Id=\"C28250\" Action=\"None\" />\n    <Rule Id=\"C28251\" Action=\"None\" />\n    <Rule Id=\"C28252\" Action=\"None\" />\n    <Rule Id=\"C28253\" Action=\"None\" />\n    <Rule Id=\"C28254\" Action=\"None\" />\n    <Rule Id=\"C28260\" Action=\"None\" />\n    <Rule Id=\"C28262\" Action=\"None\" />\n    <Rule Id=\"C28263\" Action=\"None\" />\n    <Rule Id=\"C28266\" Action=\"None\" />\n    <Rule Id=\"C28267\" Action=\"None\" />\n    <Rule Id=\"C28268\" Action=\"None\" />\n    <Rule Id=\"C28272\" Action=\"None\" />\n    <Rule Id=\"C28273\" Action=\"None\" />\n    <Rule Id=\"C28275\" Action=\"None\" />\n    <Rule Id=\"C28278\" Action=\"None\" />\n    <Rule Id=\"C28279\" Action=\"None\" />\n    <Rule Id=\"C28280\" Action=\"None\" />\n    <Rule Id=\"C28282\" Action=\"None\" />\n    <Rule Id=\"C28283\" Action=\"None\" />\n    <Rule Id=\"C28284\" Action=\"None\" />\n    <Rule Id=\"C28285\" Action=\"None\" />\n    <Rule Id=\"C28286\" Action=\"None\" />\n    <Rule Id=\"C28287\" Action=\"None\" />\n    <Rule Id=\"C28288\" Action=\"None\" />\n    <Rule Id=\"C28289\" Action=\"None\" />\n    <Rule Id=\"C28290\" Action=\"None\" />\n    <Rule Id=\"C28291\" Action=\"None\" />\n    <Rule Id=\"C28300\" Action=\"None\" />\n    <Rule Id=\"C28301\" Action=\"None\" />\n    <Rule Id=\"C28302\" Action=\"None\" />\n    <Rule Id=\"C28303\" Action=\"None\" />\n    <Rule Id=\"C28304\" Action=\"None\" />\n    <Rule Id=\"C28305\" Action=\"None\" />\n    <Rule Id=\"C28306\" Action=\"None\" />\n    <Rule Id=\"C28307\" Action=\"None\" />\n    <Rule Id=\"C28308\" Action=\"None\" />\n    <Rule Id=\"C28309\" Action=\"None\" />\n    <Rule Id=\"C28350\" Action=\"None\" />\n    <Rule Id=\"C28351\" Action=\"None\" />\n    <Rule Id=\"C28601\" Action=\"None\" />\n    <Rule Id=\"C28602\" Action=\"None\" />\n    <Rule Id=\"C28604\" Action=\"None\" />\n    <Rule Id=\"C28615\" Action=\"None\" />\n    <Rule Id=\"C28616\" Action=\"None\" />\n    <Rule Id=\"C28617\" Action=\"None\" />\n    <Rule Id=\"C28623\" Action=\"None\" />\n    <Rule Id=\"C28624\" Action=\"None\" />\n    <Rule Id=\"C28625\" Action=\"None\" />\n    <Rule Id=\"C28636\" Action=\"None\" />\n    <Rule Id=\"C28637\" Action=\"None\" />\n    <Rule Id=\"C28638\" Action=\"None\" />\n    <Rule Id=\"C28639\" Action=\"None\" />\n    <Rule Id=\"C28640\" Action=\"None\" />\n    <Rule Id=\"C28645\" Action=\"None\" />\n    <Rule Id=\"C28648\" Action=\"None\" />\n    <Rule Id=\"C28649\" Action=\"None\" />\n    <Rule Id=\"C28650\" Action=\"None\" />\n    <Rule Id=\"C28714\" Action=\"None\" />\n    <Rule Id=\"C28715\" Action=\"None\" />\n    <Rule Id=\"C28716\" Action=\"None\" />\n    <Rule Id=\"C28717\" Action=\"None\" />\n    <Rule Id=\"C28719\" Action=\"None\" />\n    <Rule Id=\"C28720\" Action=\"None\" />\n    <Rule Id=\"C28721\" Action=\"None\" />\n    <Rule Id=\"C28726\" Action=\"None\" />\n    <Rule Id=\"C28727\" Action=\"None\" />\n    <Rule Id=\"C28730\" Action=\"None\" />\n    <Rule Id=\"C28735\" Action=\"None\" />\n    <Rule Id=\"C28736\" Action=\"None\" />\n    <Rule Id=\"C28750\" Action=\"None\" />\n    <Rule Id=\"C28751\" Action=\"None\" />\n    <Rule Id=\"C6001\" Action=\"None\" />\n    <Rule Id=\"C6011\" Action=\"None\" />\n    <Rule Id=\"C6014\" Action=\"None\" />\n    <Rule Id=\"C6029\" Action=\"None\" />\n    <Rule Id=\"C6031\" Action=\"None\" />\n    <Rule Id=\"C6053\" Action=\"None\" />\n    <Rule Id=\"C6054\" Action=\"None\" />\n    <Rule Id=\"C6059\" Action=\"None\" />\n    <Rule Id=\"C6063\" Action=\"None\" />\n    <Rule Id=\"C6064\" Action=\"None\" />\n    <Rule Id=\"C6066\" Action=\"None\" />\n    <Rule Id=\"C6067\" Action=\"None\" />\n    <Rule Id=\"C6101\" Action=\"None\" />\n    <Rule Id=\"C6200\" Action=\"None\" />\n    <Rule Id=\"C6201\" Action=\"None\" />\n    <Rule Id=\"C6211\" Action=\"None\" />\n    <Rule Id=\"C6214\" Action=\"None\" />\n    <Rule Id=\"C6215\" Action=\"None\" />\n    <Rule Id=\"C6216\" Action=\"None\" />\n    <Rule Id=\"C6217\" Action=\"None\" />\n    <Rule Id=\"C6219\" Action=\"None\" />\n    <Rule Id=\"C6220\" Action=\"None\" />\n    <Rule Id=\"C6221\" Action=\"None\" />\n    <Rule Id=\"C6225\" Action=\"None\" />\n    <Rule Id=\"C6226\" Action=\"None\" />\n    <Rule Id=\"C6230\" Action=\"None\" />\n    <Rule Id=\"C6235\" Action=\"None\" />\n    <Rule Id=\"C6236\" Action=\"None\" />\n    <Rule Id=\"C6237\" Action=\"None\" />\n    <Rule Id=\"C6239\" Action=\"None\" />\n    <Rule Id=\"C6240\" Action=\"None\" />\n    <Rule Id=\"C6242\" Action=\"None\" />\n    <Rule Id=\"C6244\" Action=\"None\" />\n    <Rule Id=\"C6246\" Action=\"None\" />\n    <Rule Id=\"C6248\" Action=\"None\" />\n    <Rule Id=\"C6250\" Action=\"None\" />\n    <Rule Id=\"C6255\" Action=\"None\" />\n    <Rule Id=\"C6258\" Action=\"None\" />\n    <Rule Id=\"C6259\" Action=\"None\" />\n    <Rule Id=\"C6260\" Action=\"None\" />\n    <Rule Id=\"C6262\" Action=\"None\" />\n    <Rule Id=\"C6263\" Action=\"None\" />\n    <Rule Id=\"C6268\" Action=\"None\" />\n    <Rule Id=\"C6269\" Action=\"None\" />\n    <Rule Id=\"C6270\" Action=\"None\" />\n    <Rule Id=\"C6271\" Action=\"None\" />\n    <Rule Id=\"C6272\" Action=\"None\" />\n    <Rule Id=\"C6273\" Action=\"None\" />\n    <Rule Id=\"C6274\" Action=\"None\" />\n    <Rule Id=\"C6276\" Action=\"None\" />\n    <Rule Id=\"C6277\" Action=\"None\" />\n    <Rule Id=\"C6278\" Action=\"None\" />\n    <Rule Id=\"C6279\" Action=\"None\" />\n    <Rule Id=\"C6280\" Action=\"None\" />\n    <Rule Id=\"C6281\" Action=\"None\" />\n    <Rule Id=\"C6282\" Action=\"None\" />\n    <Rule Id=\"C6283\" Action=\"None\" />\n    <Rule Id=\"C6284\" Action=\"None\" />\n    <Rule Id=\"C6285\" Action=\"None\" />\n    <Rule Id=\"C6286\" Action=\"None\" />\n    <Rule Id=\"C6287\" Action=\"None\" />\n    <Rule Id=\"C6288\" Action=\"None\" />\n    <Rule Id=\"C6289\" Action=\"None\" />\n    <Rule Id=\"C6290\" Action=\"None\" />\n    <Rule Id=\"C6291\" Action=\"None\" />\n    <Rule Id=\"C6292\" Action=\"None\" />\n    <Rule Id=\"C6293\" Action=\"None\" />\n    <Rule Id=\"C6294\" Action=\"None\" />\n    <Rule Id=\"C6295\" Action=\"None\" />\n    <Rule Id=\"C6296\" Action=\"None\" />\n    <Rule Id=\"C6297\" Action=\"None\" />\n    <Rule Id=\"C6298\" Action=\"None\" />\n    <Rule Id=\"C6299\" Action=\"None\" />\n    <Rule Id=\"C6302\" Action=\"None\" />\n    <Rule Id=\"C6303\" Action=\"None\" />\n    <Rule Id=\"C6305\" Action=\"None\" />\n    <Rule Id=\"C6306\" Action=\"None\" />\n    <Rule Id=\"C6308\" Action=\"None\" />\n    <Rule Id=\"C6310\" Action=\"None\" />\n    <Rule Id=\"C6312\" Action=\"None\" />\n    <Rule Id=\"C6313\" Action=\"None\" />\n    <Rule Id=\"C6314\" Action=\"None\" />\n    <Rule Id=\"C6315\" Action=\"None\" />\n    <Rule Id=\"C6316\" Action=\"None\" />\n    <Rule Id=\"C6317\" Action=\"None\" />\n    <Rule Id=\"C6318\" Action=\"None\" />\n    <Rule Id=\"C6319\" Action=\"None\" />\n    <Rule Id=\"C6320\" Action=\"None\" />\n    <Rule Id=\"C6322\" Action=\"None\" />\n    <Rule Id=\"C6323\" Action=\"None\" />\n    <Rule Id=\"C6324\" Action=\"None\" />\n    <Rule Id=\"C6326\" Action=\"None\" />\n    <Rule Id=\"C6328\" Action=\"None\" />\n    <Rule Id=\"C6329\" Action=\"None\" />\n    <Rule Id=\"C6330\" Action=\"None\" />\n    <Rule Id=\"C6331\" Action=\"None\" />\n    <Rule Id=\"C6332\" Action=\"None\" />\n    <Rule Id=\"C6333\" Action=\"None\" />\n    <Rule Id=\"C6334\" Action=\"None\" />\n    <Rule Id=\"C6335\" Action=\"None\" />\n    <Rule Id=\"C6336\" Action=\"None\" />\n    <Rule Id=\"C6340\" Action=\"None\" />\n    <Rule Id=\"C6381\" Action=\"None\" />\n    <Rule Id=\"C6383\" Action=\"None\" />\n    <Rule Id=\"C6384\" Action=\"None\" />\n    <Rule Id=\"C6385\" Action=\"None\" />\n    <Rule Id=\"C6386\" Action=\"None\" />\n    <Rule Id=\"C6387\" Action=\"None\" />\n    <Rule Id=\"C6388\" Action=\"None\" />\n    <Rule Id=\"C6400\" Action=\"None\" />\n    <Rule Id=\"C6401\" Action=\"None\" />\n    <Rule Id=\"C6411\" Action=\"None\" />\n    <Rule Id=\"C6412\" Action=\"None\" />\n    <Rule Id=\"C6500\" Action=\"None\" />\n    <Rule Id=\"C6501\" Action=\"None\" />\n    <Rule Id=\"C6503\" Action=\"None\" />\n    <Rule Id=\"C6504\" Action=\"None\" />\n    <Rule Id=\"C6505\" Action=\"None\" />\n    <Rule Id=\"C6506\" Action=\"None\" />\n    <Rule Id=\"C6508\" Action=\"None\" />\n    <Rule Id=\"C6509\" Action=\"None\" />\n    <Rule Id=\"C6510\" Action=\"None\" />\n    <Rule Id=\"C6511\" Action=\"None\" />\n    <Rule Id=\"C6513\" Action=\"None\" />\n    <Rule Id=\"C6514\" Action=\"None\" />\n    <Rule Id=\"C6515\" Action=\"None\" />\n    <Rule Id=\"C6516\" Action=\"None\" />\n    <Rule Id=\"C6517\" Action=\"None\" />\n    <Rule Id=\"C6518\" Action=\"None\" />\n    <Rule Id=\"C6522\" Action=\"None\" />\n    <Rule Id=\"C6525\" Action=\"None\" />\n    <Rule Id=\"C6527\" Action=\"None\" />\n    <Rule Id=\"C6530\" Action=\"None\" />\n    <Rule Id=\"C6540\" Action=\"None\" />\n    <Rule Id=\"C6551\" Action=\"None\" />\n    <Rule Id=\"C6552\" Action=\"None\" />\n    <Rule Id=\"C6701\" Action=\"None\" />\n    <Rule Id=\"C6702\" Action=\"None\" />\n    <Rule Id=\"C6703\" Action=\"None\" />\n    <Rule Id=\"C6704\" Action=\"None\" />\n    <Rule Id=\"C6705\" Action=\"None\" />\n    <Rule Id=\"C6706\" Action=\"None\" />\n    <Rule Id=\"C6707\" Action=\"None\" />\n    <Rule Id=\"C6995\" Action=\"None\" />\n  </Rules>\n</RuleSet>"
  },
  {
    "path": "src/Common/Hangfire.targets",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup Condition=\"'$(ArtifactsDir)' != ''\">\n    <OutputPath Condition=\"$(UseBinPath) == ''\">$(ArtifactsDir)\\$(MSBuildProjectName)</OutputPath>\n    <OutputPath Condition=\"$(UseBinPath) == 'true'\">$(ArtifactsDir)\\$(MSBuildProjectName)\\bin</OutputPath>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Hangfire.ruleset</CodeAnalysisRuleSet>\n    <RunCodeAnalysis Condition=\"'$(RunCodeAnalysis)' == ''\">false</RunCodeAnalysis>\n    <NoWarn>1591</NoWarn>\n    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\"'$(RunCodeAnalysis)' == 'true'\">\n      <DefineConstants>$(DefineConstants);CODE_ANALYSIS</DefineConstants>\n      <VisualStudioVersion>11.0</VisualStudioVersion>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\"'$(OS)' != 'Windows_NT'\">\n      <DefineConstants>$(DefineConstants);MONO</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\"'$(KeyFile)' != '' And '$(DisableSigning)' != 'true'\">\n    <DefineConstants>$(DefineConstants);SIGNED</DefineConstants>\n    <SignAssembly>true</SignAssembly>\n    <DelaySign>true</DelaySign>\n    <AssemblyOriginatorKeyFile>$(KeyFile)</AssemblyOriginatorKeyFile>\n  </PropertyGroup>\n\n  <ItemGroup Condition=\"Exists('$(MSBuildThisFileDirectory)GlobalSuppressions.cs')\">\n    <Compile Include=\"$(MSBuildThisFileDirectory)GlobalSuppressions.cs\">\n        <Link>GlobalSuppressions.cs</Link>\n    </Compile>\n  </ItemGroup>\n\n  <ItemGroup>\n  <CodeAnalysisDictionary Include=\"$(MSBuildThisFileDirectory)CodeAnalysisDictionary.xml\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "src/Hangfire.PostgreSql/CountersAggregator.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Server;\n\nnamespace Hangfire.PostgreSql\n{\n#pragma warning disable 618\n  internal class CountersAggregator : IServerComponent\n#pragma warning restore 618\n  {\n    // This number should be high enough to aggregate counters efficiently,\n    // but low enough to not to cause large amount of row locks to be taken.\n    // Lock escalation to page locks may pause the background processing.\n    private const int NumberOfRecordsInSinglePass = 1000;\n\n    private static readonly TimeSpan _delayBetweenPasses = TimeSpan.FromMilliseconds(500);\n\n    private readonly ILog _logger = LogProvider.For<CountersAggregator>();\n    private readonly TimeSpan _interval;\n    private readonly PostgreSqlStorage _storage;\n\n    public CountersAggregator(PostgreSqlStorage storage, TimeSpan interval)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n      _interval = interval;\n    }\n\n    public void Execute(CancellationToken cancellationToken)\n    {\n      _logger.Debug(\"Aggregating records in 'Counter' table...\");\n\n      int removedCount = 0;\n      \n      do\n      {\n        _storage.UseConnection(null, connection => {\n          removedCount = connection.Execute(GetAggregationQuery(),\n            new { now = DateTime.UtcNow, count = NumberOfRecordsInSinglePass },\n            commandTimeout: 0);\n        });\n\n        if (removedCount < NumberOfRecordsInSinglePass)\n        {\n          continue;\n        }\n\n        cancellationToken.Wait(_delayBetweenPasses);\n        cancellationToken.ThrowIfCancellationRequested();\n        // ReSharper disable once LoopVariableIsNeverChangedInsideLoop\n      } while (removedCount >= NumberOfRecordsInSinglePass);\n\n      _logger.Trace(\"Records from the 'Counter' table aggregated.\");\n\n      cancellationToken.Wait(_interval);\n    }\n\n    private string GetAggregationQuery()\n    {\n      string schemaName = _storage.Options.SchemaName;\n      return\n        $\"\"\"\n        BEGIN;\n\n        INSERT INTO \"{schemaName}\".\"aggregatedcounter\" (\"key\", \"value\", \"expireat\")\t\n        SELECT\n          \"key\",\n          SUM(\"value\"),\n          MAX(\"expireat\")\n        FROM \"{schemaName}\".\"counter\"\n        GROUP BY \"key\"\n        ON CONFLICT(\"key\") DO UPDATE\n        SET \"value\" = \"aggregatedcounter\".\"value\" + EXCLUDED.\"value\", \"expireat\" = EXCLUDED.\"expireat\";\n  \n        DELETE FROM \"{schemaName}\".\"counter\"\n        WHERE \"key\" IN (\n          SELECT \"key\" FROM \"{schemaName}\".\"aggregatedcounter\"\n        );\n\n        COMMIT;\n        \"\"\";\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/EnqueuedAndFetchedCountDto.cs",
    "content": "// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nnamespace Hangfire.PostgreSql\n{\n  public class EnqueuedAndFetchedCountDto\n  {\n    public long EnqueuedCount { get; set; }\n    public long FetchedCount { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Entities/JobParameter.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing Hangfire.PostgreSql.Properties;\n\nnamespace Hangfire.PostgreSql.Entities\n{\n  [UsedImplicitly]\n  internal class JobParameter\n  {\n    public long JobId { get; set; }\n    public string Name { get; set; }\n    public string Value { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Entities/Server.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing Hangfire.PostgreSql.Properties;\n\nnamespace Hangfire.PostgreSql.Entities\n{\n  [UsedImplicitly]\n  internal class Server\n  {\n    public string Id { get; set; }\n    public string Data { get; set; }\n    public DateTime LastHeartbeat { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Entities/ServerData.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\n\nnamespace Hangfire.PostgreSql.Entities\n{\n  internal class ServerData\n  {\n    public int WorkerCount { get; set; }\n    public string[] Queues { get; set; }\n    public DateTime? StartedAt { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Entities/SqlHash.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing Hangfire.PostgreSql.Properties;\n\nnamespace Hangfire.PostgreSql.Entities\n{\n  [UsedImplicitly]\n  internal class SqlHash\n  {\n    public long Id { get; set; }\n    public string Key { get; set; }\n    public string Field { get; set; }\n    public string Value { get; set; }\n    public DateTime? ExpireAt { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Entities/SqlJob.cs",
    "content": "// This file is part of Hangfire.PostgreSql.\n// Copyright � 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing Hangfire.PostgreSql.Properties;\n\nnamespace Hangfire.PostgreSql.Entities\n{\n  [UsedImplicitly]\n  internal class SqlJob\n  {\n    public long Id { get; set; }\n    public string InvocationData { get; set; }\n    public string Arguments { get; set; }\n    public DateTime CreatedAt { get; set; }\n    public DateTime? ExpireAt { get; set; }\n\n    public DateTime? FetchedAt { get; set; }\n\n    public string StateName { get; set; }\n    public string StateReason { get; set; }\n    public string StateData { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Entities/SqlState.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing Hangfire.PostgreSql.Properties;\n\nnamespace Hangfire.PostgreSql.Entities\n{\n  [UsedImplicitly]\n  internal class SqlState\n  {\n    public long JobId { get; set; }\n    public string Name { get; set; }\n    public string Reason { get; set; }\n    public DateTime CreatedAt { get; set; }\n    public string Data { get; set; }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/EnvironmentHelpers.cs",
    "content": "﻿using System;\n\nnamespace Hangfire.PostgreSql\n{\n  internal class EnvironmentHelpers\n  {\n    private static bool? _isMono;\n\n    public static bool IsMono()\n    {\n      return _isMono ??= Type.GetType(\"Mono.Runtime\") != null;\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/ExpirationManager.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Data;\nusing System.Globalization;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Logging;\nusing Hangfire.Server;\nusing Hangfire.Storage;\n\nnamespace Hangfire.PostgreSql\n{\n#pragma warning disable CS0618\n  internal class ExpirationManager : IBackgroundProcess, IServerComponent\n#pragma warning restore CS0618\n  {\n    private const string DistributedLockKey = \"locks:expirationmanager\";\n\n    private static readonly TimeSpan _defaultLockTimeout = TimeSpan.FromMinutes(5);\n    private static readonly TimeSpan _delayBetweenPasses = TimeSpan.FromSeconds(1);\n    private static readonly ILog _logger = LogProvider.GetLogger(typeof(ExpirationManager));\n\n    private static readonly string[] _processedCounters = {\n      \"stats:succeeded\",\n      \"stats:deleted\",\n    };\n    private static readonly string[] _processedTables = {\n      \"aggregatedcounter\",\n      \"counter\",\n      \"job\",\n      \"list\",\n      \"set\",\n      \"hash\",\n    };\n\n    private readonly TimeSpan _checkInterval;\n    private readonly PostgreSqlStorage _storage;\n\n    public ExpirationManager(PostgreSqlStorage storage)\n      : this(storage ?? throw new ArgumentNullException(nameof(storage)), storage.Options.JobExpirationCheckInterval) { }\n\n    public ExpirationManager(PostgreSqlStorage storage, TimeSpan checkInterval)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n      _checkInterval = checkInterval;\n    }\n\n    public void Execute(BackgroundProcessContext context)\n    {\n      Execute(context.StoppingToken);\n    }\n\n    public void Execute(CancellationToken cancellationToken)\n    {\n      foreach (string table in _processedTables)\n      {\n        _logger.DebugFormat(\"Removing outdated records from table '{0}'...\", table);\n\n        UseConnectionDistributedLock(_storage, connection => {\n          int removedCount;\n          do\n          {\n            using IDbTransaction transaction = connection.BeginTransaction();\n            removedCount = connection.Execute($@\"\n                DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"{table}\"\" \n                WHERE \"\"id\"\" IN (\n                    SELECT \"\"id\"\" \n                    FROM \"\"{_storage.Options.SchemaName}\"\".\"\"{table}\"\" \n                    WHERE \"\"expireat\"\" < NOW() \n                    LIMIT {_storage.Options.DeleteExpiredBatchSize.ToString(CultureInfo.InvariantCulture)}\n                )\", transaction: transaction);\n\n            if (removedCount <= 0)\n            {\n              continue;\n            }\n\n            transaction.Commit();\n            _logger.InfoFormat(\"Removed {0} outdated record(s) from '{1}' table.\", removedCount, table);\n\n            cancellationToken.WaitHandle.WaitOne(_delayBetweenPasses);\n            cancellationToken.ThrowIfCancellationRequested();\n          }\n          while (removedCount != 0);\n        });\n      }\n\n      AggregateCounters(cancellationToken);\n      cancellationToken.WaitHandle.WaitOne(_checkInterval);\n    }\n\n    public override string ToString()\n    {\n      return \"SQL Records Expiration Manager\";\n    }\n\n    private void AggregateCounters(CancellationToken cancellationToken)\n    {\n      foreach (string processedCounter in _processedCounters)\n      {\n        AggregateCounter(processedCounter);\n        cancellationToken.ThrowIfCancellationRequested();\n      }\n    }\n\n    private void AggregateCounter(string counterName)\n    {\n      UseConnectionDistributedLock(_storage, connection => {\n        using IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);\n        string aggregateQuery = $@\"\n            WITH \"\"counters\"\" AS (\n              DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\"\n              WHERE \"\"key\"\" = @Key\n              AND \"\"expireat\"\" IS NULL\n              RETURNING *\n            )\n\n            SELECT SUM(\"\"value\"\") FROM \"\"counters\"\";\n          \";\n\n        long aggregatedValue = connection.ExecuteScalar<long>(aggregateQuery, new { Key = counterName }, transaction);\n        transaction.Commit();\n\n        if (aggregatedValue > 0)\n        {\n          string insertQuery = $@\"INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\"(\"\"key\"\", \"\"value\"\") VALUES (@Key, @Value);\";\n          connection.Execute(insertQuery, new { Key = counterName, Value = aggregatedValue });\n        }\n      });\n    }\n\n    private void UseConnectionDistributedLock(PostgreSqlStorage storage, Action<IDbConnection> action)\n    {\n      try\n      {\n        storage.UseConnection(null, connection => {\n          PostgreSqlDistributedLock.Acquire(connection, DistributedLockKey, _defaultLockTimeout, _storage.Options);\n\n          try\n          {\n            action(connection);\n          }\n          finally\n          {\n            PostgreSqlDistributedLock.Release(connection, DistributedLockKey, _storage.Options);\n          }\n        });\n      }\n      catch (DistributedLockTimeoutException e) when (e.Resource == DistributedLockKey)\n      {\n        // DistributedLockTimeoutException here doesn't mean that outdated records weren't removed.\n        // It just means another Hangfire server did this work.\n        _logger.Log(LogLevel.Debug,\n          () =>\n            $@\"An exception was thrown during acquiring distributed lock on the {DistributedLockKey} resource within {_defaultLockTimeout.TotalSeconds} seconds. Outdated records were not removed. It will be retried in {_checkInterval.TotalSeconds} seconds.\",\n          e);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Factories/ExistingNpgsqlConnectionFactory.cs",
    "content": "﻿using System;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Factories;\n\n/// <summary>\n/// Connection factory that utilizes an already-existing <see cref=\"NpgsqlConnection\"/>.\n/// </summary>\npublic sealed class ExistingNpgsqlConnectionFactory : NpgsqlInstanceConnectionFactoryBase\n{\n  private readonly NpgsqlConnection _connection;\n\n  /// <summary>\n  /// Instantiates the factory using specified <paramref name=\"connection\"/>.\n  /// </summary>\n  /// <param name=\"connection\"><see cref=\"NpgsqlConnection\"/> to use.</param>\n  /// <param name=\"options\"><see cref=\"PostgreSqlStorageOptions\"/> used for connection string verification.</param>\n  /// <exception cref=\"ArgumentNullException\"></exception>\n  public ExistingNpgsqlConnectionFactory(NpgsqlConnection connection, PostgreSqlStorageOptions options) : base(options)\n  {\n    _connection = connection ?? throw new ArgumentNullException(nameof(connection));\n    // To ensure valid connection string - throws internally\n    SetupConnectionStringBuilder(_connection.ConnectionString);\n  }\n\n  /// <inheritdoc />\n  public override NpgsqlConnection GetOrCreateConnection()\n  {\n    return _connection;\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Factories/NpgsqlConnectionFactory.cs",
    "content": "﻿using System;\nusing Hangfire.Annotations;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Factories;\n\n/// <summary>\n/// Connection factory that creates a new <see cref=\"NpgsqlConnection\"/> based on the connection string.\n/// </summary>\npublic sealed class NpgsqlConnectionFactory : NpgsqlInstanceConnectionFactoryBase\n{\n  private readonly string _connectionString;\n  [CanBeNull] private readonly Action<NpgsqlConnection> _connectionSetup;\n  [CanBeNull] private readonly Func<string> _getConnectionString;\n\n  /// <summary>\n  /// Instantiates the factory using specified <paramref name=\"connectionString\"/>.\n  /// </summary>\n  /// <param name=\"connectionString\">Connection string.</param>\n  /// <param name=\"options\"><see cref=\"PostgreSqlStorageOptions\"/> used for connection string verification.</param>\n  /// <param name=\"connectionSetup\">Optional additional connection setup action to be performed on the created <see cref=\"NpgsqlConnection\"/>.</param>\n  /// <exception cref=\"ArgumentNullException\">Throws if <paramref name=\"connectionString\"/> is null.</exception>\n  public NpgsqlConnectionFactory(string connectionString, PostgreSqlStorageOptions options, [CanBeNull] Action<NpgsqlConnection> connectionSetup = null) : base(options)\n  {\n    _connectionString = SetupConnectionStringBuilder(connectionString ?? throw new ArgumentNullException(nameof(connectionString))).ConnectionString;\n    _connectionSetup = connectionSetup;\n  }\n\n  public NpgsqlConnectionFactory(Func<string> getConnectionString, PostgreSqlStorageOptions options, [CanBeNull] Action<NpgsqlConnection> connectionSetup = null) : this(getConnectionString.Invoke(), options, connectionSetup)\n  {\n    _getConnectionString = getConnectionString;\n  }\n\n  /// <inheritdoc />\n  public override NpgsqlConnection GetOrCreateConnection()\n  {\n    var connectionString = _connectionString;\n    if (_getConnectionString != null)\n    {\n      connectionString = SetupConnectionStringBuilder(_getConnectionString.Invoke()).ConnectionString;\n    }\n    \n    NpgsqlConnection connection = new(connectionString);\n    _connectionSetup?.Invoke(connection);\n    return connection;\n  }\n}"
  },
  {
    "path": "src/Hangfire.PostgreSql/Factories/NpgsqlInstanceConnectionFactoryBase.cs",
    "content": "﻿using System;\nusing Hangfire.Annotations;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Factories;\n\npublic abstract class NpgsqlInstanceConnectionFactoryBase : IConnectionFactory\n{\n  private readonly PostgreSqlStorageOptions _options;\n  [CanBeNull] private NpgsqlConnectionStringBuilder _connectionStringBuilder;\n  [CanBeNull] private string _connectionString;\n\n  protected NpgsqlInstanceConnectionFactoryBase(PostgreSqlStorageOptions options)\n  {\n    _options = options ?? throw new ArgumentNullException(nameof(options));\n  }\n\n  /// <summary>\n  /// Gets the connection string builder associated with the current instance.\n  /// </summary>\n  /// <exception cref=\"InvalidOperationException\">Throws if connection string builder has not been initialized.</exception>\n  public NpgsqlConnectionStringBuilder ConnectionString =>\n    _connectionStringBuilder ?? throw new InvalidOperationException(\"Connection string builder has not been initialized\");\n\n  protected NpgsqlConnectionStringBuilder SetupConnectionStringBuilder(string connectionString)\n  {\n    if (_connectionStringBuilder != null && string.Equals(_connectionString, connectionString, StringComparison.OrdinalIgnoreCase))\n    {\n      return _connectionStringBuilder;\n    }\n\n    try\n    {\n      _connectionString = connectionString;\n      NpgsqlConnectionStringBuilder builder = new(connectionString);\n\n      // The connection string must not be modified when transaction enlistment is enabled, otherwise it will cause\n      // prepared transactions and probably fail when other statements (outside of hangfire) ran within the same\n      // transaction. Also see #248.\n      if (!_options.EnableTransactionScopeEnlistment && builder.Enlist)\n      {\n        throw new ArgumentException($\"TransactionScope enlistment must be enabled by setting {nameof(PostgreSqlStorageOptions)}.{nameof(PostgreSqlStorageOptions.EnableTransactionScopeEnlistment)} to `true`.\");\n      }\n\n      return _connectionStringBuilder = builder;\n    }\n    catch (ArgumentException ex)\n    {\n      throw new ArgumentException($\"Connection string is not valid\", nameof(connectionString), ex);\n    }\n  }\n\n  /// <inheritdoc />\n  public abstract NpgsqlConnection GetOrCreateConnection();\n}"
  },
  {
    "path": "src/Hangfire.PostgreSql/Hangfire.PostgreSql.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <Description>PostgreSql storage implementation for Hangfire (background job system for ASP.NET and aspnet core applications).</Description>\n    <Copyright>Copyright © 2014-2024 Frank Hommers and others</Copyright>\n    <AssemblyTitle>Hangfire PostgreSql Storage</AssemblyTitle>\n    <VersionPrefix>1.9.4</VersionPrefix>\n    <Authors>Frank Hommers, Vytautas Kasparavičius, Žygimantas Arūna</Authors>\n    <TargetFramework>netstandard2.0</TargetFramework>\n    <AssemblyName>Hangfire.PostgreSql</AssemblyName>\n    <OutputType>Library</OutputType>\n    <PackageTags>Hangfire;PostgreSql;Postgres</PackageTags>\n    <PackageId>Hangfire.PostgreSql</PackageId>\n    <PackageReleaseNotes>https://github.com/frankhommers/Hangfire.PostgreSql/releases</PackageReleaseNotes>\n    <PackageProjectUrl>http://hmm.rs/Hangfire.PostgreSql</PackageProjectUrl>\n    <PackageLicenseUrl></PackageLicenseUrl>\n    <Version>1.9.4.0</Version>\n    <FileVersion>1.9.4.0</FileVersion>\n    <AssemblyVersion>1.9.4.0</AssemblyVersion>\n    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>\n    <PackageLicenseFile>LICENSE.md</PackageLicenseFile>\n    <RepositoryUrl>https://github.com/frankhommers/Hangfire.PostgreSql</RepositoryUrl>\n    <RepositoryType>git</RepositoryType>\n    <Company>https://github.com/hangfire-postgres</Company>\n    <LangVersion>default</LangVersion>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <NoWarn>$(NoWarn);1591</NoWarn>\n    <InterceptorsNamespaces>$(InterceptorsNamespaces);Dapper.AOT</InterceptorsNamespaces>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Include=\"Scripts\\*.sql\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Dapper\" Version=\"2.0.123\" />\n    <PackageReference Include=\"Dapper.AOT\" Version=\"1.0.48\" PrivateAssets=\"buildtransitive; analyzers\" />\n    <PackageReference Include=\"GitVersion.MsBuild\" Version=\"5.11.1\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"Hangfire.Core\" Version=\"1.8.0\" />\n    <PackageReference Include=\"Microsoft.CSharp\" Version=\"4.7.0\" />\n    <PackageReference Include=\"Npgsql\" Version=\"6.0.11\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Include=\"..\\..\\LICENSE.md\">\n      <Pack>True</Pack>\n      <PackagePath></PackagePath>\n    </None>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/IConnectionFactory.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql\n{\n  /// <summary>\n  /// Connection factory for creating <see cref=\"NpgsqlConnection\"/> at runtime. \n  /// </summary>\n  public interface IConnectionFactory\n  {\n    /// <summary>\n    /// Get or create <see cref=\"NpgsqlConnection\"/>.\n    /// </summary>\n    NpgsqlConnection GetOrCreateConnection();\n  }\n}"
  },
  {
    "path": "src/Hangfire.PostgreSql/IPersistentJobQueue.cs",
    "content": "// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System.Data;\nusing System.Threading;\nusing Hangfire.Storage;\n\nnamespace Hangfire.PostgreSql\n{\n  public interface IPersistentJobQueue\n  {\n    IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken);\n    void Enqueue(IDbConnection connection, string queue, string jobId);\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/IPersistentJobQueueMonitoringApi.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System.Collections.Generic;\n\nnamespace Hangfire.PostgreSql\n{\n  public interface IPersistentJobQueueMonitoringApi\n  {\n    IEnumerable<string> GetQueues();\n    IEnumerable<long> GetEnqueuedJobIds(string queue, int from, int perPage);\n    IEnumerable<long> GetFetchedJobIds(string queue, int from, int perPage);\n    EnqueuedAndFetchedCountDto GetEnqueuedAndFetchedCount(string queue);\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/IPersistentJobQueueProvider.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nnamespace Hangfire.PostgreSql\n{\n  public interface IPersistentJobQueueProvider\n  {\n    IPersistentJobQueue GetJobQueue();\n    IPersistentJobQueueMonitoringApi GetJobQueueMonitoringApi();\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/JsonParameter.cs",
    "content": "﻿using System;\nusing System.Text.Json;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.PostgreSql;\n\ninternal static class JsonParameter\n{\n  public static string GetParameterValue([CanBeNull] object value)\n  {\n    return GetParameterValue(value, ValueType.Object);\n  }\n\n  public static string GetParameterValue([CanBeNull] object value, ValueType type)\n  {\n    return value switch {\n      string { Length: > 0 } stringValue => stringValue,\n      string { Length: 0 } or null => GetDefaultValue(type),\n      var _ => JsonSerializer.Serialize(value),\n    };\n  }\n\n  private static string GetDefaultValue(ValueType type)\n  {\n    return type switch {\n      ValueType.Object => \"{}\",\n      ValueType.Array => \"[]\",\n      var _ => throw new ArgumentOutOfRangeException(),\n    };\n  }\n\n  public enum ValueType\n  {\n    Object,\n    Array,\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PersistentJobQueueProviderCollection.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Hangfire.PostgreSql\n{\n  public class PersistentJobQueueProviderCollection : IEnumerable<IPersistentJobQueueProvider>\n  {\n    private readonly IPersistentJobQueueProvider _defaultProvider;\n\n    private readonly List<IPersistentJobQueueProvider> _providers = new();\n\n    private readonly Dictionary<string, IPersistentJobQueueProvider> _providersByQueue = new(StringComparer.OrdinalIgnoreCase);\n\n    public PersistentJobQueueProviderCollection(IPersistentJobQueueProvider defaultProvider)\n    {\n      _defaultProvider = defaultProvider ?? throw new ArgumentNullException(nameof(defaultProvider));\n      _providers.Add(_defaultProvider);\n    }\n\n    public IEnumerator<IPersistentJobQueueProvider> GetEnumerator()\n    {\n      return _providers.GetEnumerator();\n    }\n\n    IEnumerator IEnumerable.GetEnumerator()\n    {\n      return GetEnumerator();\n    }\n\n    public void Add(IPersistentJobQueueProvider provider, IEnumerable<string> queues)\n    {\n      if (provider == null)\n      {\n        throw new ArgumentNullException(nameof(provider));\n      }\n\n      if (queues == null)\n      {\n        throw new ArgumentNullException(nameof(queues));\n      }\n\n      _providers.Add(provider);\n\n      foreach (string queue in queues)\n      {\n        _providersByQueue.Add(queue, provider);\n      }\n    }\n\n    public IPersistentJobQueueProvider GetProvider(string queue)\n    {\n      return _providersByQueue.TryGetValue(queue, out IPersistentJobQueueProvider provider)\n        ? provider\n        : _defaultProvider;\n    }\n\n    public void Remove(string queue)\n    {\n      if (!_providersByQueue.ContainsKey(queue))\n      {\n        return;\n      }\n\n      IPersistentJobQueueProvider provider = _providersByQueue[queue];\n      _providersByQueue.Remove(queue);\n      _providers.Remove(provider);\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlBootstrapperConfigurationExtensions.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql\n{\n  public static class PostgreSqlBootstrapperConfigurationExtensions\n  {\n    /// <summary>\n    ///   Tells the bootstrapper to use PostgreSQL as a job storage,\n    ///   that can be accessed using the given connection string or\n    ///   its name.\n    /// </summary>\n    /// <param name=\"configuration\">Configuration</param>\n    /// <param name=\"connectionString\">Connection string</param>\n    [Obsolete(\"Will be removed in 2.0. Please use UsePostgreSqlStorage(Action<PostgreSqlBootstrapperOptions>) overload.\")]\n    public static IGlobalConfiguration<PostgreSqlStorage> UsePostgreSqlStorage(\n      this IGlobalConfiguration configuration,\n      string connectionString)\n    {\n      return configuration.UsePostgreSqlStorage(connectionString, null, new PostgreSqlStorageOptions());\n    }\n\n    /// <summary>\n    ///   Tells the bootstrapper to use PostgreSQL as a job storage\n    ///   with the given options, that can be accessed using the specified\n    ///   connection string.\n    /// </summary>\n    /// <param name=\"configuration\">Configuration</param>\n    /// <param name=\"connectionString\">Connection string</param>\n    /// <param name=\"options\">Advanced options</param>\n    [Obsolete(\"Will be removed in 2.0. Please use UsePostgreSqlStorage(Action<PostgreSqlBootstrapperOptions>, PostgreSqlStorageOptions) overload.\")]\n    public static IGlobalConfiguration<PostgreSqlStorage> UsePostgreSqlStorage(\n      this IGlobalConfiguration configuration,\n      string connectionString,\n      PostgreSqlStorageOptions options)\n    {\n      return configuration.UsePostgreSqlStorage(connectionString, null, options);\n    }\n\n    /// <summary>\n    ///   Tells the bootstrapper to use PostgreSQL as a job storage\n    ///   with the given options, that can be accessed using the specified\n    ///   connection string.\n    /// </summary>\n    /// <param name=\"configuration\">Configuration</param>\n    /// <param name=\"connectionString\">Connection string</param>\n    /// <param name=\"connectionSetup\">Optional setup action to apply to created connections</param>\n    /// <param name=\"options\">Advanced options</param>\n    [Obsolete(\"Will be removed in 2.0. Please use UsePostgreSqlStorage(Action<PostgreSqlBootstrapperOptions>, PostgreSqlStorageOptions) overload.\")]\n    public static IGlobalConfiguration<PostgreSqlStorage> UsePostgreSqlStorage(\n      this IGlobalConfiguration configuration,\n      string connectionString,\n      Action<NpgsqlConnection> connectionSetup,\n      PostgreSqlStorageOptions options)\n    {\n      return configuration.UsePostgreSqlStorage(configure => configure.UseNpgsqlConnection(connectionString, connectionSetup), options);\n    }\n\n    /// <summary>\n    ///   Tells the bootstrapper to use PostgreSQL as a job storage\n    ///   with the given options, that can be accessed using the specified\n    ///   connection factory.\n    /// </summary>\n    /// <param name=\"configuration\">Configuration</param>\n    /// <param name=\"connectionFactory\">Connection factory</param>\n    /// <param name=\"options\">Advanced options</param>\n    [Obsolete(\"Will be removed in 2.0. Please use UsePostgreSqlStorage(Action<PostgreSqlBootstrapperOptions>, PostgreSqlStorageOptions) overload.\")]\n    public static IGlobalConfiguration<PostgreSqlStorage> UsePostgreSqlStorage(\n      this IGlobalConfiguration configuration,\n      IConnectionFactory connectionFactory,\n      PostgreSqlStorageOptions options)\n    {\n      return configuration.UsePostgreSqlStorage(configure => configure.UseConnectionFactory(connectionFactory), options);\n    }\n\n    /// <summary>\n    ///   Tells the bootstrapper to use PostgreSQL as a job storage\n    ///   with the given options, that can be accessed using the specified\n    ///   connection factory.\n    /// </summary>\n    /// <param name=\"configuration\">Configuration</param>\n    /// <param name=\"connectionFactory\">Connection factory</param>\n    [Obsolete(\"Will be removed in 2.0. Please use UsePostgreSqlStorage(Action<PostgreSqlBootstrapperOptions>) overload.\")]\n    public static IGlobalConfiguration<PostgreSqlStorage> UsePostgreSqlStorage(\n      this IGlobalConfiguration configuration,\n      IConnectionFactory connectionFactory)\n    {\n      return configuration.UsePostgreSqlStorage(connectionFactory, new PostgreSqlStorageOptions());\n    }\n\n    /// <summary>\n    /// Tells the bootstrapper to use PostgreSQL as the job storage with the default storage options.\n    /// </summary>\n    /// <param name=\"configuration\">Configuration instance.</param>\n    /// <param name=\"configure\">Bootstrapper configuration action.</param>\n    /// <returns><see cref=\"IGlobalConfiguration{T}\"/> instance whose generic type argument is <see cref=\"PostgreSqlStorage\"/>.</returns>\n    public static IGlobalConfiguration<PostgreSqlStorage> UsePostgreSqlStorage(this IGlobalConfiguration configuration, Action<PostgreSqlBootstrapperOptions> configure)\n    {\n      return configuration.UsePostgreSqlStorage(configure, new PostgreSqlStorageOptions());\n    }\n\n    /// <summary>\n    /// Tells the bootstrapper to use PostgreSQL as the job storage with the specified storage options.\n    /// </summary>\n    /// <param name=\"configuration\">Configuration instance.</param>\n    /// <param name=\"configure\">Bootstrapper configuration action.</param>\n    /// <param name=\"options\">Storage options.</param>\n    /// <returns><see cref=\"IGlobalConfiguration{T}\"/> instance whose generic type argument is <see cref=\"PostgreSqlStorage\"/>.</returns>\n    /// <exception cref=\"InvalidOperationException\">Throws if <see cref=\"IConnectionFactory\"/> is not set up in the <paramref name=\"configure\"/> action.</exception>\n    public static IGlobalConfiguration<PostgreSqlStorage> UsePostgreSqlStorage(this IGlobalConfiguration configuration, Action<PostgreSqlBootstrapperOptions> configure, PostgreSqlStorageOptions options)\n    {\n      if (options == null)\n      {\n        throw new ArgumentNullException(nameof(options));\n      }\n\n      PostgreSqlBootstrapperOptions bootstrapperOptions = new(options);\n      configure(bootstrapperOptions);\n\n      IConnectionFactory connectionFactory = bootstrapperOptions.ConnectionFactory;\n      if (connectionFactory == null)\n      {\n        throw new InvalidOperationException(\"Connection factory is not specified\");\n      }\n\n      PostgreSqlStorage storage = new(connectionFactory, options);\n      return configuration.UseStorage(storage);\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlBootstrapperOptions.cs",
    "content": "﻿using System;\nusing Hangfire.Annotations;\nusing Hangfire.PostgreSql.Factories;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql;\n\n/// <summary>\n/// Bootstrapper options.\n/// </summary>\npublic class PostgreSqlBootstrapperOptions\n{\n  private readonly PostgreSqlStorageOptions _options;\n\n  internal PostgreSqlBootstrapperOptions(PostgreSqlStorageOptions options)\n  {\n    _options = options ?? throw new ArgumentNullException(nameof(options));\n  }\n\n  [CanBeNull] internal IConnectionFactory ConnectionFactory { get; private set; }\n\n  /// <summary>\n  /// Configures the bootstrapper to use a custom <see cref=\"IConnectionFactory\"/> to use for each database action.\n  /// </summary>\n  /// <param name=\"connectionFactory\">Instance of <see cref=\"IConnectionFactory\"/>.</param>\n  /// <returns>This instance.</returns>\n  /// <exception cref=\"ArgumentNullException\">Throws if <paramref name=\"connectionFactory\"/> is null.</exception>\n  public PostgreSqlBootstrapperOptions UseConnectionFactory(IConnectionFactory connectionFactory)\n  {\n    ConnectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));\n    return this;\n  }\n\n  /// <summary>\n  /// Configures the bootstrapper to create a new <see cref=\"NpgsqlConnection\"/> for each database action.\n  /// </summary>\n  /// <param name=\"connectionString\">Connection string.</param>\n  /// <param name=\"connectionSetup\">Optional additional connection setup action to be performed on the created <see cref=\"NpgsqlConnection\"/>.</param>\n  /// <returns>This instance.</returns>\n  public PostgreSqlBootstrapperOptions UseNpgsqlConnection(string connectionString, [CanBeNull] Action<NpgsqlConnection> connectionSetup = null)\n  {\n    return UseConnectionFactory(new NpgsqlConnectionFactory(connectionString, _options, connectionSetup));\n  }\n\n  public PostgreSqlBootstrapperOptions UseNpgsqlConnection(Func<string> getConnectionString, [CanBeNull] Action<NpgsqlConnection> connectionSetup = null)\n  {\n    return UseConnectionFactory(new NpgsqlConnectionFactory(getConnectionString, _options, connectionSetup));\n  }\n\n  /// <summary>\n  /// Configures the bootstrapper to use the existing <see cref=\"NpgsqlConnection\"/> for each database action.\n  /// </summary>\n  /// <param name=\"connection\"><see cref=\"NpgsqlConnection\"/> to use.</param>\n  /// <returns>This instance.</returns>\n  public PostgreSqlBootstrapperOptions UseExistingNpgsqlConnection(NpgsqlConnection connection)\n  {\n    return UseConnectionFactory(new ExistingNpgsqlConnectionFactory(connection, _options));\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlConnection.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Diagnostics;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.PostgreSql.Entities;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Npgsql;\nusing IsolationLevel = System.Transactions.IsolationLevel;\n\nnamespace Hangfire.PostgreSql\n{\n  [DapperAot]\n  public class PostgreSqlConnection : JobStorageConnection\n  {\n    private readonly Dictionary<string, HashSet<Guid>> _lockedResources;\n    private readonly PostgreSqlStorageOptions _options;\n    private readonly PostgreSqlStorage _storage;\n\n    private DbConnection _dedicatedConnection;\n\n    public PostgreSqlConnection(PostgreSqlStorage storage)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n      _options = storage.Options ?? throw new ArgumentNullException(nameof(storage.Options));\n      _lockedResources = new Dictionary<string, HashSet<Guid>>();\n    }\n\n    public override void Dispose()\n    {\n      if (_dedicatedConnection == null)\n      {\n        return;\n      }\n\n      _dedicatedConnection.Dispose();\n      _dedicatedConnection = null;\n    }\n\n    public override IWriteOnlyTransaction CreateWriteTransaction()\n    {\n      return new PostgreSqlWriteOnlyTransaction(_storage, () => _dedicatedConnection);\n    }\n\n    public override IDisposable AcquireDistributedLock(string resource, TimeSpan timeout)\n    {\n      return string.IsNullOrEmpty(resource)\n        ? throw new ArgumentNullException(nameof(resource))\n        : AcquireLock($\"{_options.SchemaName}:{resource}\", timeout);\n    }\n\n    public override IFetchedJob FetchNextJob(string[] queues, CancellationToken cancellationToken)\n    {\n      if (queues == null || queues.Length == 0)\n      {\n        throw new ArgumentNullException(nameof(queues));\n      }\n\n      IPersistentJobQueueProvider[] providers = queues\n        .Select(_storage.QueueProviders.GetProvider)\n        .Distinct()\n        .ToArray();\n\n      if (providers.Length != 1)\n      {\n        throw new InvalidOperationException(\n          $\"Multiple provider instances registered for queues: {string.Join(\", \", queues)}. You should choose only one type of persistent queues per server instance.\");\n      }\n\n      IPersistentJobQueue persistentQueue = providers[0].GetJobQueue();\n      return persistentQueue.Dequeue(queues, cancellationToken);\n    }\n\n    public override string CreateExpiredJob(\n      Job job,\n      IDictionary<string, string> parameters,\n      DateTime createdAt,\n      TimeSpan expireIn)\n    {\n      if (job == null)\n      {\n        throw new ArgumentNullException(nameof(job));\n      }\n\n      if (parameters == null)\n      {\n        throw new ArgumentNullException(nameof(parameters));\n      }\n\n      string createJobSql = $@\"\n        INSERT INTO \"\"{_options.SchemaName}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\", \"\"expireat\"\")\n        VALUES (@InvocationData::jsonb, @Arguments::jsonb, @CreatedAt, @ExpireAt) \n        RETURNING \"\"id\"\";\n      \";\n\n      InvocationData invocationData = InvocationData.SerializeJob(job);\n\n      return _storage.UseTransaction(_dedicatedConnection, (connection, transaction) => {\n        string jobId = connection.QuerySingle<long>(createJobSql,\n          new {\n            InvocationData = JsonParameter.GetParameterValue(SerializationHelper.Serialize(invocationData)),\n            Arguments = JsonParameter.GetParameterValue(invocationData.Arguments, JsonParameter.ValueType.Array),\n            CreatedAt = createdAt,\n            ExpireAt = createdAt.Add(expireIn),\n          }).ToString(CultureInfo.InvariantCulture);\n\n        if (parameters.Count > 0)\n        {\n          var parameterArray = parameters\n            .Select(parameter =>\n              new {\n                JobId = Convert.ToInt64(jobId, CultureInfo.InvariantCulture),\n                Name = parameter.Key,\n                parameter.Value,\n              }\n            )\n            .ToArray();\n\n          string insertParameterSql = $@\"\n            INSERT INTO \"\"{_options.SchemaName}\"\".\"\"jobparameter\"\" (\"\"jobid\"\", \"\"name\"\", \"\"value\"\")\n            VALUES (@JobId, @Name, @Value);\n          \";\n          connection.Execute(insertParameterSql, parameterArray, transaction);\n        }\n\n        return jobId;\n      });\n    }\n\n    public override JobData GetJobData(string id)\n    {\n      if (id == null)\n      {\n        throw new ArgumentNullException(nameof(id));\n      }\n\n      string sql = $@\"\n        SELECT \"\"invocationdata\"\" \"\"invocationData\"\", \"\"statename\"\" \"\"stateName\"\", \"\"arguments\"\", \"\"createdat\"\" \"\"createdAt\"\" \n        FROM \"\"{_options.SchemaName}\"\".\"\"job\"\" \n        WHERE \"\"id\"\" = @Id;\n      \";\n\n      SqlJob jobData = _storage.UseConnection(_dedicatedConnection,\n        connection => connection\n          .QuerySingleOrDefault<SqlJob>(sql, new { Id = Convert.ToInt64(id, CultureInfo.InvariantCulture) }));\n\n      if (jobData == null)\n      {\n        return null;\n      }\n\n      // TODO: conversion exception could be thrown.\n      InvocationData invocationData = SerializationHelper.Deserialize<InvocationData>(jobData.InvocationData);\n      invocationData.Arguments = jobData.Arguments;\n\n      Job job = null;\n      JobLoadException loadException = null;\n\n      try\n      {\n        job = invocationData.DeserializeJob();\n      }\n      catch (JobLoadException ex)\n      {\n        loadException = ex;\n      }\n\n      return new JobData {\n        Job = job,\n        State = jobData.StateName,\n        CreatedAt = jobData.CreatedAt,\n        LoadException = loadException,\n      };\n    }\n\n    public override StateData GetStateData(string jobId)\n    {\n      if (jobId == null)\n      {\n        throw new ArgumentNullException(nameof(jobId));\n      }\n\n      string sql = $@\"\n        SELECT s.\"\"name\"\" \"\"Name\"\", s.\"\"reason\"\" \"\"Reason\"\", s.\"\"data\"\" \"\"Data\"\"\n        FROM \"\"{_options.SchemaName}\"\".\"\"state\"\" s\n        INNER JOIN \"\"{_options.SchemaName}\"\".\"\"job\"\" j on j.\"\"stateid\"\" = s.\"\"id\"\"\n        WHERE j.\"\"id\"\" = @JobId;\n      \";\n\n      SqlState sqlState = _storage.UseConnection(_dedicatedConnection,\n        connection => connection\n          .QuerySingleOrDefault<SqlState>(sql, new { JobId = Convert.ToInt64(jobId, CultureInfo.InvariantCulture) }));\n      return sqlState == null\n        ? null\n        : new StateData {\n          Name = sqlState.Name,\n          Reason = sqlState.Reason,\n          Data = SerializationHelper.Deserialize<Dictionary<string, string>>(sqlState.Data),\n        };\n    }\n\n    public override void SetJobParameter(string id, string name, string value)\n    {\n      if (id == null)\n      {\n        throw new ArgumentNullException(nameof(id));\n      }\n\n      if (name == null)\n      {\n        throw new ArgumentNullException(nameof(name));\n      }\n\n      string sql = $@\"\n        WITH \"\"inputvalues\"\" AS (\n          SELECT @JobId \"\"jobid\"\", @Name \"\"name\"\", @Value \"\"value\"\"\n        ), \"\"updatedrows\"\" AS ( \n          UPDATE \"\"{_options.SchemaName}\"\".\"\"jobparameter\"\" \"\"updatetarget\"\"\n          SET \"\"value\"\" = \"\"inputvalues\"\".\"\"value\"\"\n          FROM \"\"inputvalues\"\"\n          WHERE \"\"updatetarget\"\".\"\"jobid\"\" = \"\"inputvalues\"\".\"\"jobid\"\"\n          AND \"\"updatetarget\"\".\"\"name\"\" = \"\"inputvalues\"\".\"\"name\"\"\n          RETURNING \"\"updatetarget\"\".\"\"jobid\"\", \"\"updatetarget\"\".\"\"name\"\"\n        )\n        INSERT INTO \"\"{_options.SchemaName}\"\".\"\"jobparameter\"\"(\"\"jobid\"\", \"\"name\"\", \"\"value\"\")\n        SELECT \"\"jobid\"\", \"\"name\"\", \"\"value\"\" \n        FROM \"\"inputvalues\"\" \"\"insertvalues\"\"\n        WHERE NOT EXISTS (\n          SELECT 1 \n          FROM \"\"updatedrows\"\" \n          WHERE \"\"updatedrows\"\".\"\"jobid\"\" = \"\"insertvalues\"\".\"\"jobid\"\" \n          AND \"\"updatedrows\"\".\"\"name\"\" = \"\"insertvalues\"\".\"\"name\"\"\n        );\n      \";\n\n      _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Execute(sql, new { JobId = Convert.ToInt64(id, CultureInfo.InvariantCulture), Name = name, Value = value }));\n    }\n\n    public override string GetJobParameter(string id, string name)\n    {\n      if (id == null)\n      {\n        throw new ArgumentNullException(nameof(id));\n      }\n\n      if (name == null)\n      {\n        throw new ArgumentNullException(nameof(name));\n      }\n\n      string query = $@\"SELECT \"\"value\"\" FROM \"\"{_options.SchemaName}\"\".\"\"jobparameter\"\" WHERE \"\"jobid\"\" = @Id AND \"\"name\"\" = @Name;\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<string>(query, new { Id = Convert.ToInt64(id, CultureInfo.InvariantCulture), Name = name }));\n    }\n\n    public override HashSet<string> GetAllItemsFromSet(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT \"\"value\"\" FROM \"\"{_options.SchemaName}\"\".\"\"set\"\" WHERE \"\"key\"\" = @Key;\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => {\n        IEnumerable<string> result = connection.Query<string>(query, new { Key = key });\n\n        return new HashSet<string>(result);\n      });\n    }\n\n    public override string GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      if (toScore < fromScore)\n      {\n        throw new ArgumentException($\"The '{nameof(toScore)}' value must be higher or equal to the '{nameof(fromScore)}' value.\");\n      }\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<string>($@\"\n          SELECT \"\"value\"\" \n          FROM \"\"{_options.SchemaName}\"\".\"\"set\"\" \n          WHERE \"\"key\"\" = @Key \n          AND \"\"score\"\" BETWEEN @FromScore AND @ToScore \n          ORDER BY \"\"score\"\" LIMIT 1;\n        \",\n          new { Key = key, FromScore = fromScore, ToScore = toScore }));\n    }\n\n    public override List<string> GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore, int count)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      if (toScore < fromScore)\n      {\n        throw new ArgumentException($\"The '{nameof(toScore)}' value must be higher or equal to the '{nameof(fromScore)}' value.\");\n      }\n\n      if (count < 1)\n      {\n        throw new ArgumentException($\"The '{nameof(count)}' value must be greater than zero (0).\");\n      }\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Query<string>($@\"\n          SELECT \"\"value\"\" \n          FROM \"\"{_options.SchemaName}\"\".\"\"set\"\" \n          WHERE \"\"key\"\" = @Key \n          AND \"\"score\"\" BETWEEN @FromScore AND @ToScore \n          ORDER BY \"\"score\"\" LIMIT @Limit;\n        \",\n          new { Key = key, FromScore = fromScore, ToScore = toScore, Limit = count }))\n        .AsList();\n    }\n\n    public override void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      if (keyValuePairs == null)\n      {\n        throw new ArgumentNullException(nameof(keyValuePairs));\n      }\n\n      string sql = $@\"\n        WITH \"\"inputvalues\"\" AS (\n          SELECT @Key \"\"key\"\", @Field \"\"field\"\", @Value \"\"value\"\"\n        ), \"\"updatedrows\"\" AS ( \n          UPDATE \"\"{_options.SchemaName}\"\".\"\"hash\"\" \"\"updatetarget\"\"\n          SET \"\"value\"\" = \"\"inputvalues\"\".\"\"value\"\"\n          FROM \"\"inputvalues\"\"\n          WHERE \"\"updatetarget\"\".\"\"key\"\" = \"\"inputvalues\"\".\"\"key\"\"\n          AND \"\"updatetarget\"\".\"\"field\"\" = \"\"inputvalues\"\".\"\"field\"\"\n          RETURNING \"\"updatetarget\"\".\"\"key\"\", \"\"updatetarget\"\".\"\"field\"\"\n        )\n        INSERT INTO \"\"{_options.SchemaName}\"\".\"\"hash\"\"(\"\"key\"\", \"\"field\"\", \"\"value\"\")\n        SELECT \"\"key\"\", \"\"field\"\", \"\"value\"\" FROM \"\"inputvalues\"\" \"\"insertvalues\"\"\n        WHERE NOT EXISTS (\n          SELECT 1 \n          FROM \"\"updatedrows\"\" \n          WHERE \"\"updatedrows\"\".\"\"key\"\" = \"\"insertvalues\"\".\"\"key\"\" \n          AND \"\"updatedrows\"\".\"\"field\"\" = \"\"insertvalues\"\".\"\"field\"\"\n        );\n      \";\n\n      Stopwatch executionTimer = Stopwatch.StartNew();\n      while (true)\n      {\n        try\n        {\n          _storage.UseTransaction(_dedicatedConnection, (connection, transaction) => {\n            foreach (KeyValuePair<string, string> keyValuePair in keyValuePairs)\n            {\n              connection.Execute(sql, new { Key = key, Field = keyValuePair.Key, keyValuePair.Value }, transaction);\n            }\n          }, IsolationLevel.Serializable);\n\n          return;\n        }\n        catch (PostgresException exception)\n        {\n          if (!exception.SqlState.Equals(PostgresErrorCodes.SerializationFailure)) // 40001\n          {\n            throw;\n          }\n        }\n\n        if (executionTimer.Elapsed > _options.TransactionSynchronisationTimeout)\n        {\n          throw new TimeoutException(\"SetRangeInHash experienced timeout while trying to execute transaction\");\n        }\n      }\n    }\n\n    public override Dictionary<string, string> GetAllEntriesFromHash(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      Dictionary<string, string> result = _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Query<SqlHash>($@\"\n          SELECT \"\"field\"\" \"\"Field\"\", \"\"value\"\" \"\"Value\"\" \n          FROM \"\"{_options.SchemaName}\"\".\"\"hash\"\" \n          WHERE \"\"key\"\" = @Key;\",\n          new { Key = key })\n        .ToDictionary(x => x.Field, x => x.Value));\n\n      return result.Count != 0 ? result : null;\n    }\n\n    public override void AnnounceServer(string serverId, ServerContext context)\n    {\n      if (serverId == null)\n      {\n        throw new ArgumentNullException(nameof(serverId));\n      }\n\n      if (context == null)\n      {\n        throw new ArgumentNullException(nameof(context));\n      }\n\n      ServerData data = new() {\n        WorkerCount = context.WorkerCount,\n        Queues = context.Queues,\n        StartedAt = DateTime.UtcNow,\n      };\n\n      string sql = $@\"\n        WITH \"\"inputvalues\"\" AS (\n          SELECT @Id \"\"id\"\", @Data::jsonb \"\"data\"\", NOW() \"\"lastheartbeat\"\"\n        ), \"\"updatedrows\"\" AS ( \n          UPDATE \"\"{_options.SchemaName}\"\".\"\"server\"\" \"\"updatetarget\"\"\n          SET \"\"data\"\" = \"\"inputvalues\"\".\"\"data\"\", \"\"lastheartbeat\"\" = \"\"inputvalues\"\".\"\"lastheartbeat\"\"\n          FROM \"\"inputvalues\"\"\n          WHERE \"\"updatetarget\"\".\"\"id\"\" = \"\"inputvalues\"\".\"\"id\"\"\n          RETURNING \"\"updatetarget\"\".\"\"id\"\"\n        )\n        INSERT INTO \"\"{_options.SchemaName}\"\".\"\"server\"\"(\"\"id\"\", \"\"data\"\", \"\"lastheartbeat\"\")\n        SELECT \"\"id\"\", \"\"data\"\", \"\"lastheartbeat\"\" \n        FROM \"\"inputvalues\"\" \"\"insertvalues\"\"\n        WHERE NOT EXISTS (\n          SELECT 1 \n          FROM \"\"updatedrows\"\" \n          WHERE \"\"updatedrows\"\".\"\"id\"\" = \"\"insertvalues\"\".\"\"id\"\" \n        );\n      \";\n\n      _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Execute(sql, new { Id = serverId, Data = JsonParameter.GetParameterValue(SerializationHelper.Serialize(data)) }));\n    }\n\n    public override void RemoveServer(string serverId)\n    {\n      if (serverId == null)\n      {\n        throw new ArgumentNullException(nameof(serverId));\n      }\n\n      _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Execute($@\"DELETE FROM \"\"{_options.SchemaName}\"\".\"\"server\"\" WHERE \"\"id\"\" = @Id;\", new { Id = serverId }));\n    }\n\n    public override void Heartbeat(string serverId)\n    {\n      if (serverId == null)\n      {\n        throw new ArgumentNullException(nameof(serverId));\n      }\n\n      string query = $@\"\n        UPDATE \"\"{_options.SchemaName}\"\".\"\"server\"\" \n        SET \"\"lastheartbeat\"\" = NOW() \n        WHERE \"\"id\"\" = @Id;\n      \";\n\n      int affectedRows = _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Execute(query, new { Id = serverId }));\n\n      if (affectedRows == 0)\n      {\n        throw new BackgroundServerGoneException();\n      }\n    }\n\n    public override int RemoveTimedOutServers(TimeSpan timeOut)\n    {\n      if (timeOut.Duration() != timeOut)\n      {\n        throw new ArgumentException(\"The 'timeOut' value must be positive.\", nameof(timeOut));\n      }\n\n      string query = $@\"\n        DELETE FROM \"\"{_options.SchemaName}\"\".\"\"server\"\" \n        WHERE \"\"lastheartbeat\"\" < (NOW() - INTERVAL '{((long)timeOut.TotalMilliseconds).ToString(CultureInfo.InvariantCulture)} MILLISECONDS');\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection.Execute(query));\n    }\n\n    public override long GetSetCount(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT COUNT(\"\"key\"\") FROM \"\"{_options.SchemaName}\"\".\"\"set\"\" WHERE \"\"key\"\" = @Key\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<long>(query, new { Key = key }));\n    }\n\n    public override List<string> GetAllItemsFromList(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT \"\"value\"\" FROM \"\"{_options.SchemaName}\"\".\"\"list\"\" WHERE \"\"key\"\" = @Key ORDER BY \"\"id\"\" DESC\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Query<string>(query, new { Key = key })\n        .AsList());\n    }\n\n    public override long GetCounter(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT SUM(\"\"value\"\") FROM (\n            SELECT SUM(\"\"value\"\") \"\"value\"\" FROM \"\"{_options.SchemaName}\"\".\"\"counter\"\" WHERE \"\"key\"\" = @Key\n            UNION ALL\n            SELECT SUM(\"\"value\"\") \"\"value\"\" FROM \"\"{_options.SchemaName}\"\".\"\"aggregatedcounter\"\" WHERE \"\"key\"\" = @Key) c\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n          .QuerySingleOrDefault<long?>(query, new { Key = key }) ?? 0);\n    }\n\n    public override long GetListCount(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT COUNT(\"\"id\"\") FROM \"\"{_options.SchemaName}\"\".\"\"list\"\" WHERE \"\"key\"\" = @Key\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<long>(query, new { Key = key }));\n\n    }\n\n    public override TimeSpan GetListTtl(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT min(\"\"expireat\"\") FROM \"\"{_options.SchemaName}\"\".\"\"list\"\" WHERE \"\"key\"\" = @Key\";\n\n      DateTime? result = _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<DateTime?>(query, new { Key = key }));\n\n      return !result.HasValue ? TimeSpan.FromSeconds(-1) : result.Value - DateTime.UtcNow;\n    }\n\n    public override List<string> GetRangeFromList(string key, int startingFrom, int endingAt)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"\n          SELECT \"\"value\"\" \n          FROM \"\"{_options.SchemaName}\"\".\"\"list\"\"\n          WHERE \"\"key\"\" = @Key\n          ORDER BY \"\"id\"\" DESC\n          LIMIT @Limit OFFSET @Offset\n        \";\n\n      return _storage.UseConnection(_dedicatedConnection, connection =>\n        connection\n          .Query<string>(query, new { Key = key, Limit = endingAt - startingFrom + 1, Offset = startingFrom })\n          .AsList());\n    }\n\n    public override long GetHashCount(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT COUNT(\"\"id\"\") FROM \"\"{_options.SchemaName}\"\".\"\"hash\"\" WHERE \"\"key\"\" = @Key\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingle<long>(query, new { Key = key }));\n    }\n\n    public override TimeSpan GetHashTtl(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT MIN(\"\"expireat\"\") FROM \"\"{_options.SchemaName}\"\".\"\"hash\"\" WHERE \"\"key\"\" = @Key\";\n\n      DateTime? result = _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<DateTime?>(query, new { Key = key }));\n\n      return !result.HasValue ? TimeSpan.FromSeconds(-1) : result.Value - DateTime.UtcNow;\n    }\n\n    public override List<string> GetRangeFromSet(string key, int startingFrom, int endingAt)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"\n          SELECT \"\"value\"\" \n          FROM \"\"{_options.SchemaName}\"\".\"\"set\"\"\n          WHERE \"\"key\"\" = @Key\n          ORDER BY \"\"id\"\" \n          LIMIT @Limit OFFSET @Offset\n        \";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .Query<string>(query, new { Key = key, Limit = endingAt - startingFrom + 1, Offset = startingFrom })\n        .AsList());\n    }\n\n    public override TimeSpan GetSetTtl(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string query = $@\"SELECT min(\"\"expireat\"\") FROM \"\"{_options.SchemaName}\"\".\"\"set\"\" WHERE \"\"key\"\" = @Key\";\n\n      DateTime? result = _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<DateTime?>(query, new { Key = key }));\n\n      return !result.HasValue ? TimeSpan.FromSeconds(-1) : result.Value - DateTime.UtcNow;\n    }\n\n    public override string GetValueFromHash(string key, string name)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      if (name == null)\n      {\n        throw new ArgumentNullException(nameof(name));\n      }\n\n      string query = $@\"SELECT \"\"value\"\" FROM \"\"{_options.SchemaName}\"\".\"\"hash\"\" WHERE \"\"key\"\" = @Key AND \"\"field\"\" = @Field\";\n\n      return _storage.UseConnection(_dedicatedConnection, connection => connection\n        .QuerySingleOrDefault<string>(query, new { Key = key, Field = name }));\n    }\n\n    private IDisposable AcquireLock(string resource, TimeSpan timeout)\n    {\n      _dedicatedConnection ??= _storage.CreateAndOpenConnection();\n\n      Guid lockId = Guid.NewGuid();\n\n      if (!_lockedResources.ContainsKey(resource))\n      {\n        try\n        {\n          PostgreSqlDistributedLock.Acquire(_dedicatedConnection, resource, timeout, _options);\n        }\n        catch (Exception)\n        {\n          ReleaseLock(resource, lockId, true);\n          throw;\n        }\n\n        _lockedResources.Add(resource, new HashSet<Guid>());\n      }\n\n      _lockedResources[resource].Add(lockId);\n      return new DisposableLock(this, resource, lockId);\n    }\n\n    private void ReleaseLock(string resource, Guid lockId, bool onDisposing)\n    {\n      try\n      {\n        if (!_lockedResources.TryGetValue(resource, out HashSet<Guid> resourceLocks))\n        {\n          return;\n        }\n\n        if (!resourceLocks.Contains(lockId))\n        {\n          return;\n        }\n\n        if (resourceLocks.Remove(lockId)\n          && resourceLocks.Count == 0\n          && _lockedResources.Remove(resource)\n          && _dedicatedConnection.State == ConnectionState.Open)\n        {\n          PostgreSqlDistributedLock.Release(_dedicatedConnection, resource, _options);\n        }\n      }\n      catch (Exception)\n      {\n        if (!onDisposing)\n        {\n          throw;\n        }\n      }\n      finally\n      {\n        if (_lockedResources.Count == 0)\n        {\n          _storage.ReleaseConnection(_dedicatedConnection);\n          _dedicatedConnection = null;\n        }\n      }\n    }\n\n    private class DisposableLock : IDisposable\n    {\n      private readonly PostgreSqlConnection _connection;\n      private readonly Guid _lockId;\n      private readonly string _resource;\n\n      public DisposableLock(PostgreSqlConnection connection, string resource, Guid lockId)\n      {\n        _connection = connection;\n        _resource = resource;\n        _lockId = lockId;\n      }\n\n      public void Dispose()\n      {\n        _connection.ReleaseLock(_resource, _lockId, true);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlDistributedLock.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Data;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Transactions;\nusing Dapper;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Npgsql;\nusing IsolationLevel = System.Data.IsolationLevel;\n\nnamespace Hangfire.PostgreSql\n{\n  public sealed class PostgreSqlDistributedLock\n  {\n    private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();\n\n    private static void Log(string resource, string message, Exception ex)\n    {\n      bool isConcurrencyError = ex is PostgresException { SqlState: PostgresErrorCodes.SerializationFailure };\n      _logger.Log(isConcurrencyError ? LogLevel.Trace : LogLevel.Warn, () => $\"{resource}: {message}\", ex);\n    }\n\n    internal static void Acquire(IDbConnection connection, string resource, TimeSpan timeout, PostgreSqlStorageOptions options)\n    {\n      if (connection == null)\n      {\n        throw new ArgumentNullException(nameof(connection));\n      }\n\n      if (string.IsNullOrEmpty(resource))\n      {\n        throw new ArgumentNullException(nameof(resource));\n      }\n\n      if (options == null)\n      {\n        throw new ArgumentNullException(nameof(options));\n      }\n\n      if (connection.State != ConnectionState.Open)\n      {\n        // When we are passing a closed connection to Dapper's Execute method,\n        // it kindly opens it for us, but after command execution, it will be closed\n        // automatically, and our just-acquired application lock will immediately\n        // be released. This is not behavior we want to achieve, so let's throw an\n        // exception instead.\n        throw new InvalidOperationException(\"Connection must be open before acquiring a distributed lock.\");\n      }\n\n      LockHandler.Lock(resource, timeout, connection, options);\n    }\n\n    internal static void Release(IDbConnection connection, string resource, PostgreSqlStorageOptions options)\n    {\n      if (connection == null)\n      {\n        throw new ArgumentNullException(nameof(connection));\n      }\n\n      if (resource == null)\n      {\n        throw new ArgumentNullException(nameof(resource));\n      }\n\n      if (options == null)\n      {\n        throw new ArgumentNullException(nameof(options));\n      }\n\n      if (!LockHandler.TryRemoveLock(resource, connection, options, false))\n      {\n        throw new PostgreSqlDistributedLockException(resource);\n      }\n    }\n\n    private static class LockHandler\n    {\n      public static void Lock(string resource, TimeSpan timeout, IDbConnection connection, PostgreSqlStorageOptions options)\n      {\n        Stopwatch lockAcquiringTime = Stopwatch.StartNew();\n\n        bool tryAcquireLock = true;\n        Func<IDbConnection, string, string, bool> tryLock = options.UseNativeDatabaseTransactions\n          ? TransactionLockHandler.TryLock\n          : UpdateCountLockHandler.TryLock;\n\n        while (tryAcquireLock)\n        {\n          if (connection.State != ConnectionState.Open)\n          {\n            connection.Open();\n          }\n\n          TryRemoveLock(resource, connection, options, true);\n\n          try\n          {\n            if (tryLock(connection, options.SchemaName, resource))\n            {\n              return;\n            }\n          }\n          catch (Exception ex)\n          {\n            Log(resource, \"Failed to acquire lock\", ex);\n          }\n\n          if (lockAcquiringTime.ElapsedMilliseconds > timeout.TotalMilliseconds)\n          {\n            tryAcquireLock = false;\n          }\n          else\n          {\n            int sleepDuration = (int)(timeout.TotalMilliseconds - lockAcquiringTime.ElapsedMilliseconds);\n            if (sleepDuration > 1000)\n            {\n              sleepDuration = 1000;\n            }\n\n            if (sleepDuration > 0)\n            {\n              Thread.Sleep(sleepDuration);\n            }\n            else\n            {\n              tryAcquireLock = false;\n            }\n          }\n        }\n\n        throw new PostgreSqlDistributedLockException(resource);\n      }\n\n      public static bool TryRemoveLock(string resource, IDbConnection connection, PostgreSqlStorageOptions options, bool onlyExpired)\n      {\n        IDbTransaction trx = null;\n        try\n        {\n          // Non-expired locks are removed only when releasing them. Transaction is not needed in that case.\n          if (onlyExpired && options.UseNativeDatabaseTransactions)\n          {\n            trx = TransactionLockHandler.BeginTransactionIfNotPresent(connection);\n          }\n\n          DateTime timeout = onlyExpired ? DateTime.UtcNow - options.DistributedLockTimeout : DateTime.MaxValue;\n\n          int rowsAffected = connection.Execute($@\"DELETE FROM \"\"{options.SchemaName}\"\".\"\"lock\"\" WHERE \"\"resource\"\" = @Resource AND \"\"acquired\"\" < @Timeout\",\n            new {\n              Resource = resource,\n              Timeout = timeout,\n            }, trx);\n\n          trx?.Commit();\n\n          return rowsAffected >= 0;\n        }\n        catch (Exception ex)\n        {\n          Log(resource, \"Failed to remove lock\", ex);\n          return false;\n        }\n        finally\n        {\n          trx?.Dispose();\n        }\n      }\n    }\n\n    private static class TransactionLockHandler\n    {\n      public static bool TryLock(IDbConnection connection, string schemaName, string resource)\n      {\n        IDbTransaction trx = null;\n        try\n        {\n          trx = BeginTransactionIfNotPresent(connection);\n\n          int rowsAffected = connection.Execute($@\"\n                INSERT INTO \"\"{schemaName}\"\".\"\"lock\"\"(\"\"resource\"\", \"\"acquired\"\") \n                SELECT @Resource, @Acquired\n                WHERE NOT EXISTS (\n                    SELECT 1 FROM \"\"{schemaName}\"\".\"\"lock\"\" \n                    WHERE \"\"resource\"\" = @Resource\n                )\n                ON CONFLICT DO NOTHING;\n              \",\n            new {\n              Resource = resource,\n              Acquired = DateTime.UtcNow,\n            }, trx);\n          trx?.Commit();\n\n          return rowsAffected > 0;\n        }\n        finally\n        {\n          trx?.Dispose();\n        }\n      }\n\n      [CanBeNull]\n      public static IDbTransaction BeginTransactionIfNotPresent(IDbConnection connection)\n      {\n        // If transaction scope was created outside of hangfire, the newly-opened connection is automatically enlisted into the transaction.\n        // Starting a new transaction throws \"A transaction is already in progress; nested/concurrent transactions aren't supported.\" in that case.\n        return Transaction.Current == null ? connection.BeginTransaction(IsolationLevel.ReadCommitted) : null;\n      }\n    }\n\n    private static class UpdateCountLockHandler\n    {\n      public static bool TryLock(IDbConnection connection, string schemaName, string resource)\n      {\n        connection.Execute($@\"\n              INSERT INTO \"\"{schemaName}\"\".\"\"lock\"\"(\"\"resource\"\", \"\"updatecount\"\", \"\"acquired\"\") \n              SELECT @Resource, 0, @Acquired\n              WHERE NOT EXISTS (\n                  SELECT 1 FROM \"\"{schemaName}\"\".\"\"lock\"\" \n                  WHERE \"\"resource\"\" = @Resource\n              )\n              ON CONFLICT DO NOTHING;\n            \", new {\n          Resource = resource,\n          Acquired = DateTime.UtcNow,\n        });\n\n        int rowsAffected = connection.Execute(\n          $@\"UPDATE \"\"{schemaName}\"\".\"\"lock\"\" SET \"\"updatecount\"\" = 1 WHERE \"\"updatecount\"\" = 0 AND \"\"resource\"\" = @Resource\",\n          new { Resource = resource });\n\n        return rowsAffected > 0;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlDistributedLockException.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing Hangfire.Storage;\n\nnamespace Hangfire.PostgreSql\n{\n  [Serializable]\n  public class PostgreSqlDistributedLockException : DistributedLockTimeoutException\n  {\n    public PostgreSqlDistributedLockException(string resource) : base(resource)\n    {\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlFetchedJob.cs",
    "content": "// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Logging;\nusing Hangfire.PostgreSql.Utils;\nusing Hangfire.Storage;\n\nnamespace Hangfire.PostgreSql\n{\n  public class PostgreSqlFetchedJob : IFetchedJob\n  {\n    private readonly ILog _logger = LogProvider.GetLogger(typeof(PostgreSqlFetchedJob));\n    \n    private readonly PostgreSqlStorage _storage;\n    private bool _disposed;\n    private bool _removedFromQueue;\n    private bool _requeued;\n\n    private readonly object _syncRoot = new object();\n    private long _lastHeartbeat;\n    private readonly TimeSpan _interval;\n    \n    public PostgreSqlFetchedJob(\n      PostgreSqlStorage storage,\n      long id,\n      string jobId,\n      string queue,\n      DateTime? fetchedAt)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n\n      Id = id;\n      JobId = jobId ?? throw new ArgumentNullException(nameof(jobId));\n      Queue = queue ?? throw new ArgumentNullException(nameof(queue));\n      FetchedAt = fetchedAt ?? throw new ArgumentNullException(nameof(fetchedAt));\n\n      if (storage.Options.UseSlidingInvisibilityTimeout)\n      {\n        _lastHeartbeat = TimestampHelper.GetTimestamp();\n        _interval = TimeSpan.FromSeconds(storage.Options.InvisibilityTimeout.TotalSeconds / 5);\n        storage.HeartbeatProcess.Track(this);\n      }\n    }\n\n    public long Id { get; }\n    public string Queue { get; }\n    public string JobId { get; }\n    internal DateTime? FetchedAt { get; private set; }\n\n    public void RemoveFromQueue()\n    {\n      lock (_syncRoot)\n      {\n        if (!FetchedAt.HasValue)\n        {\n          return;\n        }\n        \n        _storage.UseConnection(null, connection => connection.Execute($@\"\n          DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" WHERE \"\"id\"\" = @Id AND \"\"fetchedat\"\" = @FetchedAt;\n        \",\n          new { Id, FetchedAt }));\n\n        _removedFromQueue = true;\n      }\n    }\n\n    public void Requeue()\n    {\n      lock (_syncRoot)\n      {\n        if (!FetchedAt.HasValue)\n        {\n          return;\n        }\n\n        _storage.UseConnection(null, connection => connection.Execute($@\"\n          UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n          SET \"\"fetchedat\"\" = NULL \n          WHERE \"\"id\"\" = @Id AND \"\"fetchedat\"\" = @FetchedAt;\n        \",\n          new { Id, FetchedAt }));\n\n        FetchedAt = null;\n        _requeued = true;\n      }\n    }\n\n    public void Dispose()\n    {\n      if (_disposed)\n      {\n        return;\n      }\n\n      _disposed = true;\n\n      DisposeTimer();\n\n      lock (_syncRoot)\n      {\n        if (!_removedFromQueue && !_requeued)\n        {\n          Requeue();\n        }\n      }\n    }\n    \n    internal void DisposeTimer()\n    {\n      if (_storage.Options.UseSlidingInvisibilityTimeout)\n      {\n        _storage.HeartbeatProcess.Untrack(this);\n      }\n    }\n    \n    internal void ExecuteKeepAliveQueryIfRequired()\n    {\n      var now = TimestampHelper.GetTimestamp();\n\n      if (TimestampHelper.Elapsed(now, Interlocked.Read(ref _lastHeartbeat)) < _interval)\n      {\n        return;\n      }\n      \n      lock (_syncRoot)\n      {\n        if (!FetchedAt.HasValue)\n        {\n          return;\n        }\n\n        if (_requeued || _removedFromQueue)\n        {\n          return;\n        }\n        \n        string updateFetchAtSql = $@\"\n          UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n          SET \"\"fetchedat\"\" = NOW()\n          WHERE \"\"id\"\" = @id AND \"\"fetchedat\"\" = @fetchedAt\n          RETURNING \"\"fetchedat\"\" AS \"\"FetchedAt\"\";\n        \";\n        \n        try\n        {\n          _storage.UseConnection(null, connection =>\n          {\n            FetchedAt = connection.ExecuteScalar<DateTime?>(updateFetchAtSql,\n              new { queue = Queue, id = Id, fetchedAt = FetchedAt });\n          });\n\n          if (!FetchedAt.HasValue)\n          {\n            _logger.Warn(\n              $\"Background job identifier '{JobId}' was fetched by another worker, will not execute keep alive.\");\n          }\n\n          _logger.Trace($\"Keep-alive query for message {Id} sent\");\n          Interlocked.Exchange(ref _lastHeartbeat, now);\n        }\n        catch (Exception ex) when (ex.IsCatchableExceptionType())\n        {\n          _logger.DebugException($\"Unable to execute keep-alive query for message {Id}\", ex);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlHeartbeatProcess.cs",
    "content": "// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Threading;\nusing Hangfire.Common;\nusing Hangfire.Server;\n\nnamespace Hangfire.PostgreSql\n{\n#pragma warning disable CS0618\n  internal sealed class PostgreSqlHeartbeatProcess : IServerComponent, IBackgroundProcess\n#pragma warning restore CS0618\n  {\n    private readonly ConcurrentDictionary<PostgreSqlFetchedJob, object> _items = new();\n\n    public void Track(PostgreSqlFetchedJob item)\n    {\n      _items.TryAdd(item, null);\n    }\n\n    public void Untrack(PostgreSqlFetchedJob item)\n    {\n      _items.TryRemove(item, out var _);\n    }\n    \n    public void Execute(CancellationToken cancellationToken)\n    {\n      foreach (var item in _items)\n      {\n        item.Key.ExecuteKeepAliveQueryIfRequired();\n      }\n\n      cancellationToken.Wait(TimeSpan.FromSeconds(1));\n    }\n\n    public void Execute(BackgroundProcessContext context)\n    {\n      Execute(context.StoppingToken);\n    }\n  }\n}"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlJobQueue.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Data;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Dapper;\nusing Hangfire.PostgreSql.Properties;\nusing Hangfire.PostgreSql.Utils;\nusing Hangfire.Storage;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql\n{\n  [DapperAot]\n  public class PostgreSqlJobQueue : IPersistentJobQueue\n  {\n    private const string JobNotificationChannel = \"new_job\";\n\n    internal static readonly AutoResetEventRegistry _queueEventRegistry = new();\n    private readonly PostgreSqlStorage _storage;\n\n    public PostgreSqlJobQueue(PostgreSqlStorage storage)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n      SignalDequeue = new AutoResetEvent(false);\n      JobQueueNotification = new AutoResetEvent(false);\n    }\n\n    private AutoResetEvent SignalDequeue { get; }\n    private AutoResetEvent JobQueueNotification { get; }\n\n    [NotNull]\n    public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken)\n    {\n      Task listenTask = null;\n      CancellationTokenSource cancelListenSource = null;\n\n      if (_storage.Options.EnableLongPolling)\n      {\n        cancelListenSource = new CancellationTokenSource();\n        listenTask = ListenForNotificationsAsync(cancelListenSource.Token);\n      }\n\n      try\n      {\n        return _storage.Options.UseNativeDatabaseTransactions\n          ? Dequeue_Transaction(queues, cancellationToken)\n          : Dequeue_UpdateCount(queues, cancellationToken);\n      }\n      finally\n      {\n        try\n        {\n          cancelListenSource?.Cancel();\n          listenTask?.Wait();\n        }\n        catch (AggregateException)\n        {\n          // Swallow all exceptions from listen task cleanup.\n          // The main dequeue operation already succeeded or failed independently.\n        }\n        finally\n        {\n          cancelListenSource?.Dispose();\n        }\n      }\n    }\n\n    public void Enqueue(IDbConnection connection, string queue, string jobId)\n    {\n      string enqueueJobSql = $@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\") \n        VALUES (@JobId, @Queue);\n      \";\n\n      connection.Execute(enqueueJobSql,\n        new { JobId = Convert.ToInt64(jobId, CultureInfo.InvariantCulture), Queue = queue });\n\n      if (_storage.Options.EnableLongPolling)\n      {\n        connection.Execute($\"NOTIFY {JobNotificationChannel}\");\n      }\n    }\n\n    /// <summary>\n    ///   Signal the waiting Thread to lookup a new Job\n    /// </summary>\n    public void FetchNextJob()\n    {\n      SignalDequeue.Set();\n    }\n\n\n    [NotNull]\n    internal IFetchedJob Dequeue_Transaction(string[] queues, CancellationToken cancellationToken)\n    {\n      if (queues == null)\n      {\n        throw new ArgumentNullException(nameof(queues));\n      }\n\n      if (queues.Length == 0)\n      {\n        throw new ArgumentException(\"Queue array must be non-empty.\", nameof(queues));\n      }\n\n      long timeoutSeconds = (long)_storage.Options.InvisibilityTimeout.Negate().TotalSeconds;\n      FetchedJob fetchedJob;\n\n      string fetchJobSql = $@\"\n        UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n        SET \"\"fetchedat\"\" = NOW()\n        WHERE \"\"id\"\" = (\n          SELECT \"\"id\"\" \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n          WHERE \"\"queue\"\" = ANY (@Queues)\n          AND (\"\"fetchedat\"\" IS NULL OR \"\"fetchedat\"\" < NOW() + INTERVAL '{timeoutSeconds.ToString(CultureInfo.InvariantCulture)} SECONDS')\n          ORDER BY \"\"fetchedat\"\" NULLS FIRST, \"\"queue\"\", \"\"jobid\"\"\n          FOR UPDATE SKIP LOCKED\n          LIMIT 1\n        )\n        RETURNING \"\"id\"\" AS \"\"Id\"\", \"\"jobid\"\" AS \"\"JobId\"\", \"\"queue\"\" AS \"\"Queue\"\", \"\"fetchedat\"\" AS \"\"FetchedAt\"\";\n      \";\n\n      WaitHandle[] nextFetchIterationWaitHandles = new[] {\n        cancellationToken.WaitHandle,\n        SignalDequeue,\n        JobQueueNotification,\n      }.Concat(_queueEventRegistry.GetWaitHandles(queues)).ToArray();\n\n      do\n      {\n        cancellationToken.ThrowIfCancellationRequested();\n\n        Utils.Utils.TryExecute(() => {\n          NpgsqlConnection connection = _storage.CreateAndOpenConnection();\n\n          try\n          {\n            using NpgsqlTransaction trx = connection.BeginTransaction(IsolationLevel.ReadCommitted);\n            FetchedJob jobToFetch = connection.QuerySingleOrDefault<FetchedJob>(fetchJobSql,\n                new { Queues = queues.ToList() }, trx);\n\n            trx.Commit();\n\n            return jobToFetch;\n          }\n          catch (InvalidOperationException)\n          {\n            // thrown by .QuerySingleOrDefault(): stop the exception propagation if the fetched job was concurrently fetched by another worker\n          }\n          finally\n          {\n            _storage.ReleaseConnection(connection);\n          }\n\n          return null;\n        },\n          out fetchedJob,\n          ex => ex is PostgresException { SqlState: PostgresErrorCodes.SerializationFailure });\n\n        if (fetchedJob == null)\n        {\n          WaitHandle.WaitAny(nextFetchIterationWaitHandles, _storage.Options.QueuePollInterval);\n        }\n      }\n      while (fetchedJob == null);\n\n      return new PostgreSqlFetchedJob(_storage,\n        fetchedJob.Id,\n        fetchedJob.JobId.ToString(CultureInfo.InvariantCulture),\n        fetchedJob.Queue,\n        fetchedJob.FetchedAt);\n    }\n\n    [NotNull]\n    internal IFetchedJob Dequeue_UpdateCount(string[] queues, CancellationToken cancellationToken)\n    {\n      if (queues == null)\n      {\n        throw new ArgumentNullException(nameof(queues));\n      }\n\n      if (queues.Length == 0)\n      {\n        throw new ArgumentException(\"Queue array must be non-empty.\", nameof(queues));\n      }\n\n      long timeoutSeconds = (long)_storage.Options.InvisibilityTimeout.Negate().TotalSeconds;\n      FetchedJob markJobAsFetched = null;\n\n      string jobToFetchSql = $@\"\n        SELECT \"\"id\"\" AS \"\"Id\"\", \"\"jobid\"\" AS \"\"JobId\"\", \"\"queue\"\" AS \"\"Queue\"\", \"\"fetchedat\"\" AS \"\"FetchedAt\"\", \"\"updatecount\"\" AS \"\"UpdateCount\"\"\n        FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n        WHERE \"\"queue\"\" = ANY (@Queues)\n        AND (\"\"fetchedat\"\" IS NULL OR \"\"fetchedat\"\" < NOW() + INTERVAL '{timeoutSeconds.ToString(CultureInfo.InvariantCulture)} SECONDS')\n        ORDER BY \"\"fetchedat\"\" NULLS FIRST, \"\"queue\"\", \"\"jobid\"\"\n        LIMIT 1;\n        \";\n\n      string markJobAsFetchedSql = $@\"\n        UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n        SET \"\"fetchedat\"\" = NOW(), \n            \"\"updatecount\"\" = (\"\"updatecount\"\" + 1) % 2000000000\n        WHERE \"\"id\"\" = @Id \n        AND \"\"updatecount\"\" = @UpdateCount\n        RETURNING \"\"id\"\" AS \"\"Id\"\", \"\"jobid\"\" AS \"\"JobId\"\", \"\"queue\"\" AS \"\"Queue\"\", \"\"fetchedat\"\" AS \"\"FetchedAt\"\";\n      \";\n\n      do\n      {\n        cancellationToken.ThrowIfCancellationRequested();\n\n        FetchedJob jobToFetch = _storage.UseConnection(null, connection => connection.QuerySingleOrDefault<FetchedJob>(jobToFetchSql,\n            new { Queues = queues.ToList() })\n          );\n\n        if (jobToFetch == null)\n        {\n          WaitHandle.WaitAny(new[] {\n              cancellationToken.WaitHandle,\n              SignalDequeue,\n              JobQueueNotification,\n            },\n            _storage.Options.QueuePollInterval);\n\n          cancellationToken.ThrowIfCancellationRequested();\n        }\n        else\n        {\n          markJobAsFetched = _storage.UseConnection(null, connection => connection.QuerySingleOrDefault<FetchedJob>(markJobAsFetchedSql,\n              jobToFetch)\n          );\n        }\n      }\n      while (markJobAsFetched == null);\n\n      return new PostgreSqlFetchedJob(_storage,\n        markJobAsFetched.Id,\n        markJobAsFetched.JobId.ToString(CultureInfo.InvariantCulture),\n        markJobAsFetched.Queue,\n        markJobAsFetched.FetchedAt);\n    }\n\n    private Task ListenForNotificationsAsync(CancellationToken cancellationToken)\n    {\n      NpgsqlConnection connection = _storage.CreateAndOpenConnection();\n\n      try\n      {\n        if (!connection.SupportsNotifications())\n        {\n          return Task.CompletedTask;\n        }\n\n        // CreateAnOpenConnection can return the same connection over and over if an existing connection\n        //  is passed in the constructor of PostgreSqlStorage. We must use a separate dedicated\n        //  connection to listen for notifications.\n        string connectionString = connection.ConnectionString;\n        NpgsqlConnection clonedConnection = connection.CloneWith(connectionString);\n\n        return Task.Run(async () => {\n          NpgsqlConnection currentConnection = clonedConnection;\n          try\n          {\n            while (!cancellationToken.IsCancellationRequested)\n            {\n              try\n              {\n                if (currentConnection.State != ConnectionState.Open)\n                {\n                  await currentConnection.OpenAsync(cancellationToken);\n                }\n\n                await currentConnection.ExecuteAsync($\"LISTEN {JobNotificationChannel}\");\n                await currentConnection.WaitAsync(cancellationToken);\n                JobQueueNotification.Set();\n              }\n              catch (OperationCanceledException)\n              {\n                throw;\n              }\n              catch (Exception ex) when (ex.IsCatchableExceptionType())\n              {\n                currentConnection?.Dispose();\n                await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);\n                currentConnection = new NpgsqlConnection(connectionString);\n              }\n            }\n          }\n          catch (OperationCanceledException)\n          {\n            // Do nothing, cancellation requested so just end.\n          }\n          finally\n          {\n            currentConnection?.Dispose();\n          }\n\n        }, cancellationToken);\n\n      }\n      finally\n      {\n        _storage.ReleaseConnection(connection);\n      }\n    }\n\n    [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]\n    internal class FetchedJob\n    {\n      public long Id { get; set; }\n      public long JobId { get; set; }\n      public string Queue { get; set; }\n      public DateTime? FetchedAt { get; set; }\n      public int UpdateCount { get; set; }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlJobQueueMonitoringApi.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Dapper;\n\nnamespace Hangfire.PostgreSql\n{\n  [DapperAot]\n  internal class PostgreSqlJobQueueMonitoringApi : IPersistentJobQueueMonitoringApi\n  {\n    private readonly PostgreSqlStorage _storage;\n\n    public PostgreSqlJobQueueMonitoringApi(PostgreSqlStorage storage)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n    }\n\n    public IEnumerable<string> GetQueues()\n    {\n      string sqlQuery = $@\"SELECT DISTINCT \"\"queue\"\" FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\"\";\n      return _storage.UseConnection(null, connection => connection.Query<string>(sqlQuery).AsList());\n    }\n\n    public IEnumerable<long> GetEnqueuedJobIds(string queue, int from, int perPage)\n    {\n      return GetQueuedOrFetchedJobIds(queue, false, from, perPage);\n    }\n\n    public IEnumerable<long> GetFetchedJobIds(string queue, int from, int perPage)\n    {\n      return GetQueuedOrFetchedJobIds(queue, true, from, perPage);\n    }\n\n    public EnqueuedAndFetchedCountDto GetEnqueuedAndFetchedCount(string queue)\n    {\n      string sqlQuery = $@\"\n        SELECT (\n            SELECT COUNT(*) \n            FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n            WHERE \"\"fetchedat\"\" IS NULL \n            AND \"\"queue\"\" = @Queue\n        ) \"\"EnqueuedCount\"\", \n        (\n          SELECT COUNT(*) \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \n          WHERE \"\"fetchedat\"\" IS NOT NULL \n          AND \"\"queue\"\" = @Queue\n        ) \"\"FetchedCount\"\";\n      \";\n\n      (long enqueuedCount, long fetchedCount) = _storage.UseConnection(null, connection => \n        connection.Query<EnqueuedAndFetchedCount>(sqlQuery, new { Queue = queue })).Single();\n\n      return new EnqueuedAndFetchedCountDto {\n        EnqueuedCount = enqueuedCount,\n        FetchedCount = fetchedCount,\n      };\n    }\n\n    private IEnumerable<long> GetQueuedOrFetchedJobIds(string queue, bool fetched, int from, int perPage)\n    {\n      string sqlQuery = $@\"\n        SELECT DISTINCT j.\"\"id\"\"\n        FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" jq\n        LEFT JOIN \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" j ON jq.\"\"jobid\"\" = j.\"\"id\"\"\n        WHERE jq.\"\"queue\"\" = @Queue\n        AND jq.\"\"fetchedat\"\" {(fetched ? \"IS NOT NULL\" : \"IS NULL\")}\n        AND j.\"\"id\"\" IS NOT NULL\n        ORDER BY j.\"\"id\"\"\n        LIMIT @Limit OFFSET @Offset;\n      \";\n\n      return _storage.UseConnection(null, connection => connection.Query<long>(sqlQuery,\n          new { Queue = queue, Offset = from, Limit = perPage })\n        .AsList());\n    }\n\n    internal record struct EnqueuedAndFetchedCount(long EnqueuedCount, long FetchedCount);\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlJobQueueProvider.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\n\nnamespace Hangfire.PostgreSql\n{\n  public class PostgreSqlJobQueueProvider : IPersistentJobQueueProvider\n  {\n    public PostgreSqlJobQueueProvider(PostgreSqlStorage storage, PostgreSqlStorageOptions options)\n    {\n      Storage = storage ?? throw new ArgumentNullException(nameof(storage));\n      Options = options ?? throw new ArgumentNullException(nameof(options));\n    }\n\n    public PostgreSqlStorageOptions Options { get; }\n    public PostgreSqlStorage Storage { get; }\n\n    public IPersistentJobQueue GetJobQueue()\n    {\n      return new PostgreSqlJobQueue(Storage);\n    }\n\n    public IPersistentJobQueueMonitoringApi GetJobQueueMonitoringApi()\n    {\n      return new PostgreSqlJobQueueMonitoringApi(Storage);\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlMonitoringApi.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Globalization;\nusing System.Linq;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.PostgreSql.Entities;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Hangfire.Storage.Monitoring;\n\nnamespace Hangfire.PostgreSql\n{\n  [DapperAot]\n  public class PostgreSqlMonitoringApi : IMonitoringApi\n  {\n    private readonly PersistentJobQueueProviderCollection _queueProviders;\n    private readonly PostgreSqlStorage _storage;\n\n    public PostgreSqlMonitoringApi(\n      PostgreSqlStorage storage,\n      PersistentJobQueueProviderCollection queueProviders)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n      _queueProviders = queueProviders ?? throw new ArgumentNullException(nameof(queueProviders));\n    }\n\n    public long ScheduledCount()\n    {\n      return GetNumberOfJobsByStateName(ScheduledState.StateName);\n    }\n\n    public long EnqueuedCount(string queue)\n    {\n      IPersistentJobQueueMonitoringApi queueApi = GetQueueApi(queue);\n      EnqueuedAndFetchedCountDto counters = queueApi.GetEnqueuedAndFetchedCount(queue);\n\n      return counters.EnqueuedCount;\n    }\n\n    public long FetchedCount(string queue)\n    {\n      IPersistentJobQueueMonitoringApi queueApi = GetQueueApi(queue);\n      EnqueuedAndFetchedCountDto counters = queueApi.GetEnqueuedAndFetchedCount(queue);\n\n      return counters.FetchedCount;\n    }\n\n    public long FailedCount()\n    {\n      return GetNumberOfJobsByStateName(FailedState.StateName);\n    }\n\n    public long ProcessingCount()\n    {\n      return GetNumberOfJobsByStateName(ProcessingState.StateName);\n    }\n\n    public JobList<ProcessingJobDto> ProcessingJobs(int from, int count)\n    {\n      return GetJobs(from, count,\n        ProcessingState.StateName,\n        (_, job, stateData) => new ProcessingJobDto {\n          Job = job,\n          ServerId = stateData.TryGetValue(\"ServerId\", out string serverId) ? serverId : stateData[\"ServerName\"],\n          StartedAt = JobHelper.DeserializeDateTime(stateData[\"StartedAt\"]),\n        });\n    }\n\n    public JobList<ScheduledJobDto> ScheduledJobs(int from, int count)\n    {\n      return GetJobs(from, count,\n        ScheduledState.StateName,\n        (_, job, stateData) => new ScheduledJobDto {\n          Job = job,\n          EnqueueAt = JobHelper.DeserializeDateTime(stateData[\"EnqueueAt\"]),\n          ScheduledAt = JobHelper.DeserializeDateTime(stateData[\"ScheduledAt\"]),\n        });\n    }\n\n    public IDictionary<DateTime, long> SucceededByDatesCount()\n    {\n      return GetTimelineStats(\"succeeded\");\n    }\n\n    public IDictionary<DateTime, long> FailedByDatesCount()\n    {\n      return GetTimelineStats(\"failed\");\n    }\n\n    public IList<ServerDto> Servers()\n    {\n      return UseConnection(connection => {\n        List<(Entities.Server Server, ServerData Data)> servers = connection.Query<Entities.Server>($@\"SELECT * FROM \"\"{_storage.Options.SchemaName}\"\".\"\"server\"\"\")\n          .AsEnumerable()\n          .Select(server => (server, SerializationHelper.Deserialize<ServerData>(server.Data)))\n          .ToList();\n\n        List<ServerDto> result = servers.Select(item => new ServerDto {\n          Name = item.Server.Id,\n          Heartbeat = item.Server.LastHeartbeat,\n          Queues = item.Data.Queues,\n          StartedAt = item.Data.StartedAt ?? DateTime.MinValue,\n          WorkersCount = item.Data.WorkerCount,\n        }).ToList();\n\n        return result;\n      });\n    }\n\n    public JobList<FailedJobDto> FailedJobs(int from, int count)\n    {\n      return GetJobs(from,\n        count,\n        FailedState.StateName,\n        (sqlJob, job, stateData) => new FailedJobDto {\n          Job = job,\n          Reason = sqlJob.StateReason,\n          ExceptionDetails = stateData[\"ExceptionDetails\"],\n          ExceptionMessage = stateData[\"ExceptionMessage\"],\n          ExceptionType = stateData[\"ExceptionType\"],\n          FailedAt = JobHelper.DeserializeNullableDateTime(stateData[\"FailedAt\"]),\n        });\n    }\n\n    public JobList<SucceededJobDto> SucceededJobs(int from, int count)\n    {\n      return GetJobs(from,\n        count,\n        SucceededState.StateName,\n        (_, job, stateData) => new SucceededJobDto {\n          Job = job,\n          Result = stateData.TryGetValue(\"Result\", out string result) ? result : null,\n          TotalDuration = stateData.ContainsKey(\"PerformanceDuration\") && stateData.TryGetValue(\"Latency\", out string latency)\n            ? long.Parse(stateData[\"PerformanceDuration\"]) + (long?)long.Parse(latency)\n            : null,\n          SucceededAt = JobHelper.DeserializeNullableDateTime(stateData[\"SucceededAt\"]),\n        });\n    }\n\n    public JobList<DeletedJobDto> DeletedJobs(int from, int count)\n    {\n      return GetJobs(from,\n        count,\n        DeletedState.StateName,\n        (_, job, stateData) => new DeletedJobDto {\n          Job = job,\n          DeletedAt = JobHelper.DeserializeNullableDateTime(stateData[\"DeletedAt\"]),\n        });\n    }\n\n    public IList<QueueWithTopEnqueuedJobsDto> Queues()\n    {\n      var tuples = _queueProviders\n        .Select(x => x.GetJobQueueMonitoringApi())\n        .SelectMany(x => x.GetQueues(), (monitoring, queue) => new { Monitoring = monitoring, Queue = queue })\n        .OrderBy(x => x.Queue)\n        .ToArray();\n\n      List<QueueWithTopEnqueuedJobsDto> result = new(tuples.Length);\n\n      foreach (var tuple in tuples)\n      {\n        IEnumerable<long> enqueuedJobIds = tuple.Monitoring.GetEnqueuedJobIds(tuple.Queue, 0, 5);\n        EnqueuedAndFetchedCountDto counters = tuple.Monitoring.GetEnqueuedAndFetchedCount(tuple.Queue);\n\n        result.Add(new QueueWithTopEnqueuedJobsDto {\n          Name = tuple.Queue,\n          Length = counters.EnqueuedCount,\n          Fetched = counters.FetchedCount,\n          FirstJobs = EnqueuedJobs(enqueuedJobIds),\n        });\n      }\n\n      return result;\n    }\n\n    public JobList<EnqueuedJobDto> EnqueuedJobs(string queue, int from, int perPage)\n    {\n      IPersistentJobQueueMonitoringApi queueApi = GetQueueApi(queue);\n      IEnumerable<long> enqueuedJobIds = queueApi.GetEnqueuedJobIds(queue, from, perPage);\n\n      return EnqueuedJobs(enqueuedJobIds);\n    }\n\n    public JobList<FetchedJobDto> FetchedJobs(string queue, int from, int perPage)\n    {\n      IPersistentJobQueueMonitoringApi queueApi = GetQueueApi(queue);\n      IEnumerable<long> fetchedJobIds = queueApi.GetFetchedJobIds(queue, from, perPage);\n\n      return FetchedJobs(fetchedJobIds);\n    }\n\n    public IDictionary<DateTime, long> HourlySucceededJobs()\n    {\n      return GetHourlyTimelineStats(\"succeeded\");\n    }\n\n    public IDictionary<DateTime, long> HourlyFailedJobs()\n    {\n      return GetHourlyTimelineStats(\"failed\");\n    }\n\n    public JobDetailsDto JobDetails(string jobId)\n    {\n      return UseConnection(connection => {\n        string sql = $@\"\n          SELECT \"\"id\"\" \"\"Id\"\", \"\"invocationdata\"\" \"\"InvocationData\"\", \"\"arguments\"\" \"\"Arguments\"\", \"\"createdat\"\" \"\"CreatedAt\"\", \"\"expireat\"\" \"\"ExpireAt\"\" \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" \n          WHERE \"\"id\"\" = @Id;\n\n          SELECT \"\"jobid\"\" \"\"JobId\"\", \"\"name\"\" \"\"Name\"\", \"\"value\"\" \"\"Value\"\" \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"jobparameter\"\" \n          WHERE \"\"jobid\"\" = @Id;\n\n          SELECT \"\"jobid\"\" \"\"JobId\"\", \"\"name\"\" \"\"Name\"\", \"\"reason\"\" \"\"Reason\"\", \"\"createdat\"\" \"\"CreatedAt\"\", \"\"data\"\" \"\"Data\"\" \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"state\"\" \n          WHERE \"\"jobid\"\" = @Id \n          ORDER BY \"\"id\"\" DESC;\n        \";\n        using SqlMapper.GridReader multi = connection.QueryMultiple(sql, new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture) });\n        SqlJob job = multi.Read<SqlJob>().SingleOrDefault();\n        if (job == null)\n        {\n          return null;\n        }\n\n        Dictionary<string, string> parameters = multi.Read<JobParameter>().ToDictionary(x => x.Name, x => x.Value);\n        List<StateHistoryDto> history =\n          multi.Read<SqlState>()\n            .ToList()\n            .Select(x => new StateHistoryDto {\n              StateName = x.Name,\n              CreatedAt = x.CreatedAt,\n              Reason = x.Reason,\n              Data = new SafeDictionary<string, string>(SerializationHelper.Deserialize<Dictionary<string, string>>(x.Data),\n                StringComparer.OrdinalIgnoreCase),\n            })\n            .ToList();\n\n        return new JobDetailsDto {\n          CreatedAt = job.CreatedAt,\n          Job = DeserializeJob(job.InvocationData, job.Arguments),\n          History = history,\n          Properties = parameters,\n          ExpireAt = job.ExpireAt,\n        };\n      });\n    }\n\n    public long SucceededListCount()\n    {\n      return GetNumberOfJobsByStateName(SucceededState.StateName);\n    }\n\n    public long DeletedListCount()\n    {\n      return GetNumberOfJobsByStateName(DeletedState.StateName);\n    }\n\n    public StatisticsDto GetStatistics()\n    {\n      return UseConnection(connection => {\n        string sql = $@\"\n          SELECT \"\"statename\"\" \"\"State\"\", COUNT(\"\"id\"\") \"\"Count\"\" \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\"\n          WHERE \"\"statename\"\" IS NOT NULL\n          GROUP BY \"\"statename\"\";\n\n          SELECT COUNT(*) \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"server\"\";\n\n          SELECT SUM(\"\"value\"\") FROM\n            (SELECT SUM(\"\"value\"\") AS value\n            FROM \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\" \n            WHERE \"\"key\"\" = 'stats:succeeded'\n            UNION ALL\n            SELECT SUM(\"\"value\"\") AS value\n            FROM \"\"{_storage.Options.SchemaName}\"\".\"\"aggregatedcounter\"\" \n            WHERE \"\"key\"\" = 'stats:succeeded') c;\n\n          SELECT SUM(\"\"value\"\") FROM\n            (SELECT SUM(\"\"value\"\") AS value\n            FROM \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\" \n            WHERE \"\"key\"\" = 'stats:deleted'\n            UNION ALL\n            SELECT SUM(\"\"value\"\") AS value\n            FROM \"\"{_storage.Options.SchemaName}\"\".\"\"aggregatedcounter\"\" \n            WHERE \"\"key\"\" = 'stats:deleted') c;\n\n          SELECT COUNT(*) \n          FROM \"\"{_storage.Options.SchemaName}\"\".\"\"set\"\" \n          WHERE \"\"key\"\" = 'recurring-jobs';\n        \";\n\n        StatisticsDto stats = new();\n        using (SqlMapper.GridReader multi = connection.QueryMultiple(sql))\n        {\n          Dictionary<string, long> countByStates = multi.Read<(string StateName, long Count)>()\n            .ToDictionary(x => x.StateName, x => x.Count);\n\n          long GetCountIfExists(string name)\n          {\n            return countByStates.TryGetValue(name, out long stateCount) ? stateCount : 0;\n          }\n\n          stats.Enqueued = GetCountIfExists(EnqueuedState.StateName);\n          stats.Failed = GetCountIfExists(FailedState.StateName);\n          stats.Processing = GetCountIfExists(ProcessingState.StateName);\n          stats.Scheduled = GetCountIfExists(ScheduledState.StateName);\n\n          stats.Servers = multi.ReadSingle<long>();\n\n          stats.Succeeded = multi.ReadSingleOrDefault<long?>() ?? 0;\n          stats.Deleted = multi.ReadSingleOrDefault<long?>() ?? 0;\n\n          stats.Recurring = multi.ReadSingle<long>();\n        }\n\n        stats.Queues = _queueProviders\n          .SelectMany(x => x.GetJobQueueMonitoringApi().GetQueues())\n          .Count();\n\n        return stats;\n      });\n    }\n\n    private Dictionary<DateTime, long> GetHourlyTimelineStats(string type)\n    {\n      DateTime endDate = DateTime.UtcNow;\n      List<DateTime> dates = new();\n      for (int i = 0; i < 24; i++)\n      {\n        dates.Add(endDate);\n        endDate = endDate.AddHours(-1);\n      }\n\n      Dictionary<string, DateTime> keyMaps = dates.ToDictionary(x => $\"stats:{type}:{x:yyyy-MM-dd-HH}\", x => x);\n\n      return GetTimelineStats(keyMaps);\n    }\n\n    private Dictionary<DateTime, long> GetTimelineStats(string type)\n    {\n      DateTime endDate = DateTime.UtcNow.Date;\n      List<DateTime> dates = new();\n\n      for (int i = 0; i < 7; i++)\n      {\n        dates.Add(endDate);\n        endDate = endDate.AddDays(-1);\n      }\n\n      Dictionary<string, DateTime> keyMaps = dates.ToDictionary(x => $\"stats:{type}:{x:yyyy-MM-dd}\", x => x);\n\n      return GetTimelineStats(keyMaps);\n    }\n\n    private Dictionary<DateTime, long> GetTimelineStats(IDictionary<string, DateTime> keyMaps)\n    {\n      string query =\n        $\"\"\"\n        WITH \"aggregated_counters\" AS (\n          SELECT \"key\", \"value\"\n          FROM \"{_storage.Options.SchemaName}\".\"aggregatedcounter\"\n          WHERE \"key\" = ANY(@Keys)\n        ), \"regular_counters\" AS (\n          SELECT \"key\", \"value\"\n          FROM \"{_storage.Options.SchemaName}\".\"counter\"\n          WHERE \"key\" = ANY(@Keys)\n        ), \"all_counters\" AS (\n          SELECT * FROM \"aggregated_counters\"\n          UNION ALL\n          SELECT * FROM \"regular_counters\"\n        )\n        SELECT \"key\", COALESCE(SUM(\"value\"), 0) AS \"count\"\n        FROM \"all_counters\"\n        GROUP BY \"key\"\n        \"\"\";\n\n      Dictionary<string, long> valuesMap = UseConnection(connection => connection.Query<KeyCount>(query,\n          new { Keys = keyMaps.Keys.ToList() })\n        .AsList()\n        .ToDictionary(x => x.Key, x => x.Count));\n\n      foreach (string key in keyMaps.Keys)\n      {\n        if (!valuesMap.ContainsKey(key))\n        {\n          valuesMap.Add(key, 0);\n        }\n      }\n\n      Dictionary<DateTime, long> result = new();\n      foreach (KeyValuePair<string, DateTime> keyMap in keyMaps)\n      {\n        long value = valuesMap[keyMap.Key];\n        result.Add(keyMap.Value, value);\n      }\n\n      return result;\n    }\n\n    internal record struct KeyCount(string Key, long Count);\n\n    private IPersistentJobQueueMonitoringApi GetQueueApi(string queueName)\n    {\n      IPersistentJobQueueProvider provider = _queueProviders.GetProvider(queueName);\n      IPersistentJobQueueMonitoringApi monitoringApi = provider.GetJobQueueMonitoringApi();\n\n      return monitoringApi;\n    }\n\n    private JobList<EnqueuedJobDto> EnqueuedJobs(IEnumerable<long> jobIds)\n    {\n      string enqueuedJobsSql = $@\"\n        SELECT DISTINCT ON (\"\"j\"\".\"\"id\"\") \"\"j\"\".\"\"id\"\" \"\"Id\"\", \"\"j\"\".\"\"invocationdata\"\" \"\"InvocationData\"\", \"\"j\"\".\"\"arguments\"\" \"\"Arguments\"\", \"\"j\"\".\"\"createdat\"\" \"\"CreatedAt\"\",\n          \"\"j\"\".\"\"expireat\"\" \"\"ExpireAt\"\", \"\"s\"\".\"\"name\"\" \"\"StateName\"\", \"\"s\"\".\"\"reason\"\" \"\"StateReason\"\", \"\"s\"\".\"\"data\"\" \"\"StateData\"\"\n        FROM \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" \"\"j\"\"\n        LEFT JOIN \"\"{_storage.Options.SchemaName}\"\".\"\"state\"\" \"\"s\"\" ON \"\"s\"\".\"\"id\"\" = \"\"j\"\".\"\"stateid\"\"\n        LEFT JOIN \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \"\"jq\"\" ON \"\"jq\"\".\"\"jobid\"\" = \"\"j\"\".\"\"id\"\"\n        WHERE \"\"j\"\".\"\"id\"\" = ANY (@JobIds)\n        AND \"\"jq\"\".\"\"fetchedat\"\" IS NULL\n        ORDER BY \"\"j\"\".\"\"id\"\";\n      \";\n\n      List<SqlJob> jobs = UseConnection(connection => connection.Query<SqlJob>(enqueuedJobsSql,\n          new { JobIds = jobIds.ToList() })\n        .AsList());\n\n      return DeserializeJobs(jobs,\n        (sqlJob, job, stateData) => new EnqueuedJobDto {\n          Job = job,\n          State = sqlJob.StateName,\n          EnqueuedAt = sqlJob.StateName == EnqueuedState.StateName\n            ? JobHelper.DeserializeNullableDateTime(stateData[\"EnqueuedAt\"])\n            : null,\n        });\n    }\n\n    private long GetNumberOfJobsByStateName(string stateName)\n    {\n      string sqlQuery = $@\"SELECT COUNT(\"\"id\"\") FROM \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" WHERE \"\"statename\"\" = @StateName;\";\n\n      return UseConnection(connection => connection.QuerySingle<long>(sqlQuery,\n        new { StateName = stateName }));\n    }\n\n    private static Job DeserializeJob(string invocationData, string arguments)\n    {\n      InvocationData data = SerializationHelper.Deserialize<InvocationData>(invocationData);\n      data.Arguments = arguments;\n\n      try\n      {\n        return data.DeserializeJob();\n      }\n      catch (JobLoadException)\n      {\n        return null;\n      }\n    }\n\n    private JobList<TDto> GetJobs<TDto>(int from, int count, string stateName, Func<SqlJob, Job, Dictionary<string, string>, TDto> selector)\n    {\n      string jobsSql = $@\"\n        SELECT \"\"j\"\".\"\"id\"\" \"\"Id\"\", \"\"j\"\".\"\"invocationdata\"\" \"\"InvocationData\"\", \"\"j\"\".\"\"arguments\"\" \"\"Arguments\"\", \"\"j\"\".\"\"createdat\"\" \"\"CreatedAt\"\", \n          \"\"j\"\".\"\"expireat\"\" \"\"ExpireAt\"\", NULL \"\"FetchedAt\"\", \"\"j\"\".\"\"statename\"\" \"\"StateName\"\", \"\"s\"\".\"\"reason\"\" \"\"StateReason\"\", \"\"s\"\".\"\"data\"\" \"\"StateData\"\"\n        FROM \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" \"\"j\"\"\n        LEFT JOIN \"\"{_storage.Options.SchemaName}\"\".\"\"state\"\" \"\"s\"\" ON \"\"j\"\".\"\"stateid\"\" = \"\"s\"\".\"\"id\"\"\n        WHERE \"\"j\"\".\"\"statename\"\" = @StateName \n        ORDER BY \"\"j\"\".\"\"id\"\" DESC\n        LIMIT @Limit OFFSET @Offset;\n      \";\n\n      List<SqlJob> jobs = UseConnection(connection => connection.Query<SqlJob>(jobsSql,\n          new { StateName = stateName, Limit = count, Offset = from })\n        .AsList());\n\n      return DeserializeJobs(jobs, selector);\n    }\n\n    private static JobList<TDto> DeserializeJobs<TDto>(\n      ICollection<SqlJob> jobs,\n      Func<SqlJob, Job, SafeDictionary<string, string>, TDto> selector)\n    {\n      List<KeyValuePair<string, TDto>> result = new(jobs.Count);\n\n      foreach (SqlJob job in jobs)\n      {\n        TDto dto = default;\n\n        if (job.InvocationData != null)\n        {\n          Dictionary<string, string> deserializedData = SerializationHelper.Deserialize<Dictionary<string, string>>(job.StateData);\n          SafeDictionary<string, string> stateData = deserializedData != null\n            ? new SafeDictionary<string, string>(deserializedData, StringComparer.OrdinalIgnoreCase)\n            : null;\n\n          dto = selector(job, DeserializeJob(job.InvocationData, job.Arguments), stateData);\n        }\n\n        result.Add(new KeyValuePair<string, TDto>(job.Id.ToString(), dto));\n      }\n\n      return new JobList<TDto>(result);\n    }\n\n    private JobList<FetchedJobDto> FetchedJobs(\n      IEnumerable<long> jobIds)\n    {\n      string fetchedJobsSql = $@\"\n        SELECT DISTINCT ON (\"\"j\"\".\"\"id\"\") \"\"j\"\".\"\"id\"\" \"\"Id\"\", \"\"j\"\".\"\"invocationdata\"\" \"\"InvocationData\"\", \"\"j\"\".\"\"arguments\"\" \"\"Arguments\"\",\n          \"\"j\"\".\"\"createdat\"\" \"\"CreatedAt\"\", \"\"j\"\".\"\"expireat\"\" \"\"ExpireAt\"\", \"\"jq\"\".\"\"fetchedat\"\" \"\"FetchedAt\"\",\n          \"\"j\"\".\"\"statename\"\" \"\"StateName\"\", \"\"s\"\".\"\"reason\"\" \"\"StateReason\"\", \"\"s\"\".\"\"data\"\" \"\"StateData\"\"\n        FROM \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" \"\"j\"\"\n        LEFT JOIN \"\"{_storage.Options.SchemaName}\"\".\"\"state\"\" \"\"s\"\" ON \"\"j\"\".\"\"stateid\"\" = \"\"s\"\".\"\"id\"\"\n        LEFT JOIN \"\"{_storage.Options.SchemaName}\"\".\"\"jobqueue\"\" \"\"jq\"\" ON \"\"jq\"\".\"\"jobid\"\" = \"\"j\"\".\"\"id\"\"\n        WHERE \"\"j\"\".\"\"id\"\" = ANY (@JobIds)\n        AND \"\"jq\"\".\"\"fetchedat\"\" IS NOT NULL\n        ORDER BY \"\"j\"\".\"\"id\"\", \"\"jq\"\".\"\"fetchedat\"\" DESC;\n      \";\n\n      List<SqlJob> jobs = UseConnection(connection => connection.Query<SqlJob>(fetchedJobsSql,\n          new { JobIds = jobIds.ToList() })\n        .AsList());\n\n      Dictionary<string, FetchedJobDto> result = jobs.ToDictionary(job => job.Id.ToString(), job => new FetchedJobDto {\n        Job = DeserializeJob(job.InvocationData, job.Arguments),\n        State = job.StateName,\n        FetchedAt = job.FetchedAt,\n      });\n\n      return new JobList<FetchedJobDto>(result);\n    }\n\n    private T UseConnection<T>(Func<IDbConnection, T> func)\n    {\n      return _storage.UseConnection(null, func);\n    }\n\n    /// <summary>\n    ///   Overloaded dictionary that doesn't throw if given an invalid key\n    ///   Fixes issues such as https://github.com/frankhommers/Hangfire.PostgreSql/issues/79\n    /// </summary>\n    private class SafeDictionary<TKey, TValue> : Dictionary<TKey, TValue>\n    {\n      public SafeDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)\n        : base(dictionary, comparer) { }\n\n      public new TValue this[TKey i]\n      {\n        // ReSharper disable once ArrangeDefaultValueWhenTypeNotEvident\n        get => ContainsKey(i) ? base[i] : default;\n        // ReSharper disable once UnusedMember.Local\n        set => base[i] = value;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlObjectsInstaller.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Data;\nusing System.Globalization;\nusing System.IO;\nusing System.Reflection;\nusing System.Resources;\nusing Hangfire.Logging;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql\n{\n  public static class PostgreSqlObjectsInstaller\n  {\n    private static readonly ILog _logger = LogProvider.GetLogger(typeof(PostgreSqlStorage));\n\n    public static void Install(NpgsqlConnection connection, string schemaName = \"hangfire\")\n    {\n      if (connection == null)\n      {\n        throw new ArgumentNullException(nameof(connection));\n      }\n\n      _logger.Info(\"Start installing Hangfire SQL objects...\");\n\n      // starts with version 3 to keep in check with Hangfire SqlServer, but I couldn't keep up with that idea after all;\n      int version = 3;\n      int previousVersion = 1;\n      do\n      {\n        try\n        {\n          string script;\n          try\n          {\n            script = GetStringResource(typeof(PostgreSqlObjectsInstaller).GetTypeInfo().Assembly,\n              $\"Hangfire.PostgreSql.Scripts.Install.v{version.ToString(CultureInfo.InvariantCulture)}.sql\");\n          }\n          catch (MissingManifestResourceException)\n          {\n            break;\n          }\n\n          if (schemaName != \"hangfire\")\n          {\n            script = script.Replace(\"'hangfire'\", $\"'{schemaName}'\").Replace(@\"\"\"hangfire\"\"\", $@\"\"\"{schemaName}\"\"\");\n          }\n\n          if (!VersionAlreadyApplied(connection, schemaName, version))\n          {\n            string commandText = $@\"{script}; UPDATE \"\"{schemaName}\"\".\"\"schema\"\" SET \"\"version\"\" = @Version WHERE \"\"version\"\" = @PreviousVersion\";\n            using NpgsqlTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable);\n            using NpgsqlCommand command = new(commandText, connection, transaction);\n            command.CommandTimeout = 120;\n            command.Parameters.Add(new NpgsqlParameter(\"Version\", version));\n            command.Parameters.Add(new NpgsqlParameter(\"PreviousVersion\", previousVersion));\n            try\n            {\n              command.ExecuteNonQuery();\n              transaction.Commit();\n            }\n            catch (PostgresException ex)\n            {\n              if ((ex.MessageText ?? \"\") != \"version-already-applied\")\n              {\n                throw;\n              }\n            }\n          }\n        }\n        catch (Exception ex)\n        {\n          if (ex.Source.Equals(\"Npgsql\"))\n          {\n            _logger.ErrorException(\"Error while executing install/upgrade\", ex);\n          }\n\n          throw;\n        }\n\n        previousVersion = version;\n        version++;\n      } while (true);\n\n      _logger.Info(\"Hangfire SQL objects installed.\");\n    }\n\n    private static bool VersionAlreadyApplied(NpgsqlConnection connection, string schemaName, int version)\n    {\n      try\n      {\n        using NpgsqlCommand command = new($@\"SELECT true \"\"VersionAlreadyApplied\"\" FROM \"\"{schemaName}\"\".\"\"schema\"\" WHERE \"\"version\"\" >= $1\", connection);\n        command.Parameters.Add(new NpgsqlParameter { Value = version });\n        object result = command.ExecuteScalar();\n        if (true.Equals(result))\n        {\n          return true;\n        }\n      }\n      catch (PostgresException ex)\n      {\n        if (ex.SqlState.Equals(PostgresErrorCodes.UndefinedTable)) //42P01: Relation (table) does not exist. So no schema table yet.\n        {\n          return false;\n        }\n\n        throw;\n      }\n\n      return false;\n    }\n\n    private static string GetStringResource(Assembly assembly, string resourceName)\n    {\n      using Stream stream = assembly.GetManifestResourceStream(resourceName);\n      if (stream == null)\n      {\n        throw new MissingManifestResourceException($\"Requested resource `{resourceName}` was not found in the assembly `{assembly}`.\");\n      }\n\n      using StreamReader reader = new(stream);\n      return reader.ReadToEnd();\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlStorage.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Text;\nusing System.Threading;\nusing System.Transactions;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Hangfire.PostgreSql.Factories;\nusing Hangfire.PostgreSql.Utils;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Npgsql;\nusing IsolationLevel = System.Transactions.IsolationLevel;\n\nnamespace Hangfire.PostgreSql\n{\n  public class PostgreSqlStorage : JobStorage\n  {\n    private readonly IConnectionFactory _connectionFactory;\n    private readonly object _initializationLock = new();\n    private bool _initialized;\n    private Exception _lastInitializationException;\n\n    private readonly Dictionary<string, bool> _features =\n      new(StringComparer.OrdinalIgnoreCase)\n      {\n        { JobStorageFeatures.JobQueueProperty, true },\n        { JobStorageFeatures.Connection.BatchedGetFirstByLowest, true }\n      };\n\n    [Obsolete(\"Will be removed in 2.0, please use the overload with IConnectionFactory argument\")]\n    public PostgreSqlStorage(string connectionString) : this(connectionString, new PostgreSqlStorageOptions()) { }\n\n    [Obsolete(\"Will be removed in 2.0, please use the overload with IConnectionFactory argument\")]\n    public PostgreSqlStorage(string connectionString, PostgreSqlStorageOptions options) : this(connectionString, null, options) { }\n\n    /// <summary>\n    ///   Initializes PostgreSqlStorage from the provided PostgreSqlStorageOptions and either the provided connection string.\n    /// </summary>\n    /// <param name=\"connectionString\">PostgreSQL connection string</param>\n    /// <param name=\"connectionSetup\">Optional setup action to apply to created connections</param>\n    /// <param name=\"options\">Storage options</param>\n    /// <exception cref=\"ArgumentNullException\"><paramref name=\"connectionString\" /> argument is null.</exception>\n    /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\" /> argument is null.</exception>\n    /// <exception cref=\"ArgumentException\"><paramref name=\"connectionString\" /> argument not a valid PostgreSQL connection string config file.</exception>\n    [Obsolete(\"Will be removed in 2.0, please use the overload with IConnectionFactory argument\")]\n    public PostgreSqlStorage(string connectionString, Action<NpgsqlConnection> connectionSetup, PostgreSqlStorageOptions options) : this(new NpgsqlConnectionFactory(connectionString, options, connectionSetup), options) { }\n\n    [Obsolete(\"Will be removed in 2.0, please use the overload with IConnectionFactory argument\")]\n    public PostgreSqlStorage(NpgsqlConnection existingConnection) : this(existingConnection, new PostgreSqlStorageOptions()) { }\n\n    /// <summary>\n    ///   Initializes a new instance of the <see cref=\"PostgreSqlStorage\" /> class with\n    ///   explicit instance of the <see cref=\"NpgsqlConnection\" /> class that will be used\n    ///   to query the data.\n    /// </summary>\n    /// <param name=\"existingConnection\">Existing connection</param>\n    /// <param name=\"options\">PostgreSqlStorageOptions</param>\n    [Obsolete(\"Will be removed in 2.0, please use the overload with IConnectionFactory argument\")]\n    public PostgreSqlStorage(NpgsqlConnection existingConnection, PostgreSqlStorageOptions options) : this(new ExistingNpgsqlConnectionFactory(existingConnection, options), options) { }\n\n    public PostgreSqlStorage(IConnectionFactory connectionFactory) : this(connectionFactory, new PostgreSqlStorageOptions()) { }\n\n    public PostgreSqlStorage(IConnectionFactory connectionFactory, PostgreSqlStorageOptions options)\n    {\n      _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));\n      Options = options ?? throw new ArgumentNullException(nameof(options));\n\n      InitializeQueueProviders();\n      if (Options.UseSlidingInvisibilityTimeout)\n      {\n        HeartbeatProcess = new PostgreSqlHeartbeatProcess();\n      }\n\n      // Perform eager initialization if schema preparation is requested. This can be made resilient\n      // via the options exposed on PostgreSqlStorageOptions.\n      if (Options.PrepareSchemaIfNecessary)\n      {\n        TryInitializeStorage(isStartup: true);\n      }\n    }\n\n    public PersistentJobQueueProviderCollection QueueProviders { get; internal set; }\n\n    internal PostgreSqlStorageOptions Options { get; }\n\n    internal PostgreSqlHeartbeatProcess HeartbeatProcess { get; }\n\n    public override IMonitoringApi GetMonitoringApi()\n    {\n      EnsureInitialized();\n      return new PostgreSqlMonitoringApi(this, QueueProviders);\n    }\n\n    public override IStorageConnection GetConnection()\n    {\n      EnsureInitialized();\n      return new PostgreSqlConnection(this);\n    }\n\n#pragma warning disable CS0618\n    public override IEnumerable<IServerComponent> GetComponents()\n#pragma warning restore CS0618\n    {\n      yield return new ExpirationManager(this);\n      yield return new CountersAggregator(this, Options.CountersAggregateInterval);\n      if (Options.UseSlidingInvisibilityTimeout)\n      {\n        // This is only used to update the sliding invisibility timeouts, so if not enabled then do not use it\n        yield return HeartbeatProcess;\n      }\n    }\n\n    public override void WriteOptionsToLog(ILog logger)\n    {\n      logger.Info(\"Using the following options for PostgreSQL job storage:\");\n      logger.InfoFormat(\"    Queue poll interval: {0}.\", Options.QueuePollInterval);\n      logger.InfoFormat(\"    Invisibility timeout: {0}.\", Options.InvisibilityTimeout);\n      logger.InfoFormat(\"    Use sliding invisibility timeout: {0}.\", Options.UseSlidingInvisibilityTimeout);\n    }\n\n    public override string ToString()\n    {\n      const string canNotParseMessage = \"<Connection string can not be parsed>\";\n\n      try\n      {\n        StringBuilder builder = new();\n\n        if (_connectionFactory is NpgsqlInstanceConnectionFactoryBase connectionFactory)\n        {\n          NpgsqlConnectionStringBuilder connectionStringBuilder = connectionFactory.ConnectionString;\n          builder.Append(\"Host: \");\n          builder.Append(connectionStringBuilder.Host);\n          builder.Append(\", DB: \");\n          builder.Append(connectionStringBuilder.Database);\n          builder.Append(\", \");\n        }\n\n        builder.Append(\"Schema: \");\n        builder.Append(Options.SchemaName);\n\n        return builder.Length != 0 ? $\"PostgreSQL Server: {builder}\" : canNotParseMessage;\n      }\n      catch (Exception)\n      {\n        return canNotParseMessage;\n      }\n    }\n\n    internal NpgsqlConnection CreateAndOpenConnection()\n    {\n      NpgsqlConnection connection = _connectionFactory.GetOrCreateConnection();\n\n      try\n      {\n        if (connection.State == ConnectionState.Closed)\n        {\n          connection.Open();\n        }\n\n        if (Options.EnableLongPolling && !connection.SupportsNotifications())\n        {\n          throw new InvalidOperationException(\"Long polling is supported only with PostgreSQL version 11 or higher.\");\n        }\n\n        return connection;\n      }\n      catch\n      {\n        ReleaseConnection(connection);\n        throw;\n      }\n    }\n\n    /// <summary>\n    /// Ensures storage is initialized. When resilient startup and degraded mode are enabled,\n    /// this will attempt a lazy initialization on first use.\n    /// </summary>\n    private void EnsureInitialized()\n    {\n      if (_initialized || !Options.PrepareSchemaIfNecessary)\n      {\n        return;\n      }\n\n      lock (_initializationLock)\n      {\n        if (_initialized)\n        {\n          return;\n        }\n\n        TryInitializeStorage(isStartup: false);\n\n        if (!_initialized && !Options.AllowDegradedModeWithoutStorage)\n        {\n          // Initialization failed and degraded mode is not enabled - rethrow with the last error\n          // to give a clear signal to the caller.\n          throw new InvalidOperationException(\n            \"Hangfire PostgreSQL storage is not initialized. See inner exception for details.\",\n            _lastInitializationException);\n        }\n      }\n    }\n\n    private void TryInitializeStorage(bool isStartup)\n    {\n      // Fast-path: no resilient startup configured - keep the current behavior of a single attempt.\n      if (!Options.EnableResilientStartup)\n      {\n        PerformSingleInitializationAttempt();\n        _initialized = true;\n        _lastInitializationException = null;\n        return;\n      }\n\n      int attempts = 0;\n      int maxAttempts = 1 + Options.StartupConnectionMaxRetries; // initial + retries\n      Exception lastException = null;\n\n      while (attempts < maxAttempts)\n      {\n        try\n        {\n          PerformSingleInitializationAttempt();\n          _initialized = true;\n          _lastInitializationException = null;\n          return;\n        }\n        catch (Exception ex)\n        {\n          lastException = ex;\n          attempts++;\n\n          if (attempts >= maxAttempts)\n          {\n            break;\n          }\n\n          // Apply exponential backoff with capping.\n          TimeSpan delay = ComputeBackoffDelay(attempts, Options.StartupConnectionBaseDelay, Options.StartupConnectionMaxDelay);\n\n          try\n          {\n            Thread.Sleep(delay);\n          }\n          catch (ThreadInterruptedException)\n          {\n            // Preserve original exception and abort initialization.\n            break;\n          }\n        }\n      }\n\n      _initialized = false;\n      _lastInitializationException = lastException;\n\n      if (!Options.AllowDegradedModeWithoutStorage && isStartup)\n      {\n        // During startup without degraded mode, fail fast to avoid starting the app in\n        // a partially configured state.\n        throw new InvalidOperationException(\n          \"Failed to initialize Hangfire PostgreSQL storage.\",\n          lastException);\n      }\n\n      // When degraded mode is allowed, we swallow the exception here and leave storage\n      // uninitialized. Subsequent calls will attempt to initialize lazily via EnsureInitialized.\n    }\n\n    private void PerformSingleInitializationAttempt()\n    {\n      NpgsqlConnection connection = CreateAndOpenConnection();\n      try\n      {\n        PostgreSqlObjectsInstaller.Install(connection, Options.SchemaName);\n      }\n      finally\n      {\n        if (_connectionFactory is not ExistingNpgsqlConnectionFactory)\n        {\n          connection.Dispose();\n        }\n      }\n    }\n\n    private static TimeSpan ComputeBackoffDelay(int attempt, TimeSpan baseDelay, TimeSpan maxDelay)\n    {\n      if (attempt <= 0)\n      {\n        return baseDelay;\n      }\n\n      double factor = Math.Pow(2, attempt - 1);\n      double millis = baseDelay.TotalMilliseconds * factor;\n\n      if (millis < 0 || millis > maxDelay.TotalMilliseconds || double.IsInfinity(millis) || double.IsNaN(millis))\n      {\n        millis = maxDelay.TotalMilliseconds;\n      }\n\n      return TimeSpan.FromMilliseconds(millis);\n    }\n\n    internal void UseTransaction(DbConnection dedicatedConnection,\n      [InstantHandle] Action<DbConnection, IDbTransaction> action,\n      IsolationLevel? isolationLevel = null)\n    {\n      UseTransaction(dedicatedConnection, (connection, transaction) => {\n        action(connection, transaction);\n        return true;\n      }, isolationLevel);\n    }\n\n    internal T UseTransaction<T>(DbConnection dedicatedConnection,\n      [InstantHandle] Func<DbConnection, IDbTransaction, T> func,\n      IsolationLevel? isolationLevel = null)\n    {\n      // Use isolation level of an already opened transaction in order to avoid isolation level conflict\n      isolationLevel ??= Transaction.Current?.IsolationLevel ?? IsolationLevel.ReadCommitted;\n\n      if (!EnvironmentHelpers.IsMono())\n      {\n        T result = UseConnection(dedicatedConnection, connection => {\n\n          using TransactionScope transaction = CreateTransactionScope(isolationLevel);\n          connection.EnlistTransaction(Transaction.Current);\n          T result = func(connection, null);\n          transaction.Complete();\n          return result;\n\n        });\n\n        return result;\n      }\n\n      return UseConnection(dedicatedConnection, connection => {\n        System.Data.IsolationLevel transactionIsolationLevel = ConvertIsolationLevel(isolationLevel) ?? System.Data.IsolationLevel.ReadCommitted;\n        using DbTransaction transaction = connection.BeginTransaction(transactionIsolationLevel);\n        T result;\n\n        try\n        {\n          result = func(connection, transaction);\n          transaction.Commit();\n        }\n        catch\n        {\n          if (transaction.Connection != null)\n          {\n            // Don't rely on implicit rollback when calling the Dispose\n            // method, because some implementations may throw the\n            // NullReferenceException, although it's prohibited to throw\n            // any exception from a Dispose method, according to the\n            // .NET Framework Design Guidelines:\n            // https://github.com/dotnet/efcore/issues/12864\n            // https://github.com/HangfireIO/Hangfire/issues/1494\n            transaction.Rollback();\n          }\n\n          throw;\n        }\n\n        return result;\n      });\n    }\n\n    internal void UseTransaction(DbConnection dedicatedConnection, Action<DbConnection, DbTransaction> action, Func<TransactionScope> transactionScopeFactory)\n    {\n      UseTransaction(dedicatedConnection, (connection, transaction) => {\n        action(connection, transaction);\n        return true;\n      }, transactionScopeFactory);\n    }\n\n    internal T UseTransaction<T>(DbConnection dedicatedConnection, Func<DbConnection, DbTransaction, T> func, Func<TransactionScope> transactionScopeFactory)\n    {\n      return UseConnection(dedicatedConnection, connection => {\n        using TransactionScope transaction = transactionScopeFactory();\n        connection.EnlistTransaction(Transaction.Current);\n\n        T result = func(connection, null);\n\n        transaction.Complete();\n\n        return result;\n      });\n    }\n\n    internal TransactionScope CreateTransactionScope(IsolationLevel? isolationLevel, TimeSpan? timeout = null)\n    {\n      return TransactionHelpers.CreateTransactionScope(isolationLevel, Options.EnableTransactionScopeEnlistment, timeout);\n    }\n\n    private static System.Data.IsolationLevel? ConvertIsolationLevel(IsolationLevel? isolationLevel)\n    {\n      return isolationLevel switch {\n        IsolationLevel.Chaos => System.Data.IsolationLevel.Chaos,\n        IsolationLevel.ReadCommitted => System.Data.IsolationLevel.ReadCommitted,\n        IsolationLevel.ReadUncommitted => System.Data.IsolationLevel.ReadUncommitted,\n        IsolationLevel.RepeatableRead => System.Data.IsolationLevel.RepeatableRead,\n        IsolationLevel.Serializable => System.Data.IsolationLevel.Serializable,\n        IsolationLevel.Snapshot => System.Data.IsolationLevel.Snapshot,\n        IsolationLevel.Unspecified => System.Data.IsolationLevel.Unspecified,\n        null => null,\n        var _ => throw new ArgumentOutOfRangeException(nameof(isolationLevel), isolationLevel, null),\n      };\n    }\n\n    internal void UseConnection(DbConnection dedicatedConnection, [InstantHandle] Action<DbConnection> action)\n    {\n      UseConnection(dedicatedConnection, connection => {\n        action(connection);\n        return true;\n      });\n    }\n\n    internal T UseConnection<T>(DbConnection dedicatedConnection, Func<DbConnection, T> func)\n    {\n      DbConnection connection = null;\n\n      try\n      {\n        connection = dedicatedConnection ?? CreateAndOpenConnection();\n        return func(connection);\n      }\n      finally\n      {\n        if (dedicatedConnection == null)\n        {\n          ReleaseConnection(connection);\n        }\n      }\n    }\n\n    internal void ReleaseConnection(DbConnection connection)\n    {\n      if (connection != null && !IsExistingConnection(connection))\n      {\n        connection.Dispose();\n      }\n    }\n\n    private bool IsExistingConnection(IDbConnection connection)\n    {\n      return connection != null && _connectionFactory is ExistingNpgsqlConnectionFactory && ReferenceEquals(connection, _connectionFactory.GetOrCreateConnection());\n    }\n\n    private void InitializeQueueProviders()\n    {\n      PostgreSqlJobQueueProvider defaultQueueProvider = new(this, Options);\n      QueueProviders = new PersistentJobQueueProviderCollection(defaultQueueProvider);\n    }\n\n    public override bool HasFeature(string featureId)\n    {\n      if (featureId == null)\n      {\n        throw new ArgumentNullException(nameof(featureId));\n      }\n\n      return _features.TryGetValue(featureId, out bool isSupported) \n        ? isSupported\n        : base.HasFeature(featureId);\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlStorageOptions.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\n\nnamespace Hangfire.PostgreSql\n{\n  public class PostgreSqlStorageOptions\n  {\n    private static readonly TimeSpan _minimumQueuePollInterval = TimeSpan.FromMilliseconds(50);\n\n    private int _deleteExpiredBatchSize;\n    private TimeSpan _distributedLockTimeout;\n    private TimeSpan _invisibilityTimeout;\n    private TimeSpan _jobExpirationCheckInterval;\n    private TimeSpan _queuePollInterval;\n    private TimeSpan _transactionSerializationTimeout;\n    private TimeSpan _countersAggregateInterval;\n\n    public PostgreSqlStorageOptions()\n    {\n      QueuePollInterval = TimeSpan.FromSeconds(15);\n      InvisibilityTimeout = TimeSpan.FromMinutes(30);\n      DistributedLockTimeout = TimeSpan.FromMinutes(10);\n      TransactionSynchronisationTimeout = TimeSpan.FromMilliseconds(500);\n      JobExpirationCheckInterval = TimeSpan.FromHours(1);\n      CountersAggregateInterval = TimeSpan.FromMinutes(5);\n      SchemaName = \"hangfire\";\n      AllowUnsafeValues = false;\n      UseNativeDatabaseTransactions = true;\n      PrepareSchemaIfNecessary = true;\n      EnableTransactionScopeEnlistment = true;\n      DeleteExpiredBatchSize = 1000;\n      UseSlidingInvisibilityTimeout = false;\n      StartupConnectionMaxRetries = 5;\n      StartupConnectionBaseDelay = TimeSpan.FromSeconds(1);\n      StartupConnectionMaxDelay = TimeSpan.FromMinutes(1);\n      AllowDegradedModeWithoutStorage = true;\n    }\n\n    public TimeSpan QueuePollInterval\n    {\n      get => _queuePollInterval;\n      set {\n        ThrowIfValueIsLowerThan(_minimumQueuePollInterval, value, nameof(QueuePollInterval));\n        _queuePollInterval = value;\n      }\n    }\n\n    public TimeSpan InvisibilityTimeout\n    {\n      get => _invisibilityTimeout;\n      set {\n        ThrowIfValueIsNotPositive(value, nameof(InvisibilityTimeout));\n        _invisibilityTimeout = value;\n      }\n    }\n    \n    public TimeSpan DistributedLockTimeout\n    {\n      get => _distributedLockTimeout;\n      set {\n        ThrowIfValueIsNotPositive(value, nameof(DistributedLockTimeout));\n        _distributedLockTimeout = value;\n      }\n    }\n\n    // ReSharper disable once IdentifierTypo\n    public TimeSpan TransactionSynchronisationTimeout\n    {\n      get => _transactionSerializationTimeout;\n      set {\n        ThrowIfValueIsNotPositive(value, nameof(TransactionSynchronisationTimeout));\n        _transactionSerializationTimeout = value;\n      }\n    }\n\n    public TimeSpan JobExpirationCheckInterval\n    {\n      get => _jobExpirationCheckInterval;\n      set {\n        ThrowIfValueIsNotPositive(value, nameof(JobExpirationCheckInterval));\n        _jobExpirationCheckInterval = value;\n      }\n    }\n\n    public TimeSpan CountersAggregateInterval\n    {\n      get => _countersAggregateInterval;\n      set {\n        ThrowIfValueIsNotPositive(value, nameof(CountersAggregateInterval));\n        _countersAggregateInterval = value;\n      }\n    }\n\n    /// <summary>\n    ///   Gets or sets the number of records deleted in a single batch in expiration manager\n    /// </summary>\n    public int DeleteExpiredBatchSize\n    {\n      get => _deleteExpiredBatchSize;\n      set {\n        ThrowIfValueIsNotPositive(value, nameof(DeleteExpiredBatchSize));\n        _deleteExpiredBatchSize = value;\n      }\n    }\n\n    public bool AllowUnsafeValues { get; set; }\n    public bool UseNativeDatabaseTransactions { get; set; }\n    public bool PrepareSchemaIfNecessary { get; set; }\n    public string SchemaName { get; set; }\n    public bool EnableTransactionScopeEnlistment { get; set; }\n    public bool EnableLongPolling { get; set; }\n\n    /// <summary>\n    ///   Apply a sliding invisibility timeout where the last fetched time is continually updated in the background.\n    ///   This allows a lower invisibility timeout to be used with longer running jobs\n    ///   IMPORTANT: If <see cref=\"BackgroundJobServerOptions.IsLightweightServer\" /> option is used, then sliding invisiblity timeouts will not work\n    ///   since the background storage processes are not run (which is used to update the invisibility timeouts)\n    /// </summary>\n    public bool UseSlidingInvisibilityTimeout { get; set; }\n\n    /// <summary>\n    /// Gets if additional resilience during storage initialization is enabled. When <see cref=\"StartupConnectionMaxRetries\"/>\n    /// is greater than zero and <see cref=\"PrepareSchemaIfNecessary\"/> is true, Hangfire will retry opening a\n    /// connection and installing schema instead of failing immediately.\n    /// This property is computed from <see cref=\"StartupConnectionMaxRetries\"/>.\n    /// </summary>\n    public bool EnableResilientStartup => StartupConnectionMaxRetries > 0;\n\n    /// <summary>\n    /// Maximum number of additional attempts (after the initial one) to obtain a connection and\n    /// prepare the schema during startup when <see cref=\"EnableResilientStartup\"/> is true.\n    /// Value of 0 keeps current behavior (no retries).\n    /// </summary>\n    public int StartupConnectionMaxRetries { get; set; }\n\n    /// <summary>\n    /// Base delay used to compute exponential backoff between startup connection attempts when <see cref=\"EnableResilientStartup\"/> is true.\n    /// </summary>\n    public TimeSpan StartupConnectionBaseDelay { get; set; }\n\n    /// <summary>\n    /// Maximum delay between startup connection attempts when <see cref=\"EnableResilientStartup\"/> is true.\n    /// </summary>\n    public TimeSpan StartupConnectionMaxDelay { get; set; }\n\n    /// <summary>\n    /// When true and <see cref=\"EnableResilientStartup\"/> is enabled, storage initialization will\n    /// not throw even if all startup connection attempts fail. Instead, the storage starts in a\n    /// degraded mode and will attempt to initialize lazily on first use.\n    /// </summary>\n    public bool AllowDegradedModeWithoutStorage { get; set; }\n\n    private static void ThrowIfValueIsNotPositive(TimeSpan value, string fieldName)\n    {\n      string message = $\"The {fieldName} property value should be positive. Given: {value}.\";\n\n      if (value == TimeSpan.Zero)\n      {\n        throw new ArgumentException(message, nameof(value));\n      }\n\n      if (value != value.Duration())\n      {\n        throw new ArgumentException(message, nameof(value));\n      }\n    }\n\n    private void ThrowIfValueIsLowerThan(TimeSpan minValue, TimeSpan value, string fieldName)\n    {\n      if (!AllowUnsafeValues)\n      {\n        string message = $\"The {fieldName} property value seems to be too low ({value}, lower than suggested minimum of {minValue}). Consider increasing it. If you really need to have such a low value, please set {nameof(PostgreSqlStorageOptions)}.{nameof(AllowUnsafeValues)} to true.\";\n\n        if (value < minValue)\n        {\n          throw new ArgumentException(message, nameof(value));\n        }\n      }\n\n      ThrowIfValueIsNotPositive(value, fieldName);\n    }\n\n    private static void ThrowIfValueIsNotPositive(int value, string fieldName)\n    {\n      if (value <= 0)\n      {\n        throw new ArgumentException($\"The {fieldName} property value should be positive. Given: {value}.\");\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/PostgreSqlWriteOnlyTransaction.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Linq;\nusing System.Transactions;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire.PostgreSql\n{\n  public class PostgreSqlWriteOnlyTransaction : JobStorageTransaction\n  {\n    private readonly Queue<Action<IDbConnection>> _commandQueue = new();\n    private readonly Func<DbConnection> _dedicatedConnectionFunc;\n    private readonly List<string> _queuesWithAddedJobs = new();\n\n    private readonly PostgreSqlStorage _storage;\n\n    public PostgreSqlWriteOnlyTransaction(\n      PostgreSqlStorage storage,\n      Func<DbConnection> dedicatedConnectionFunc)\n    {\n      _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n      _dedicatedConnectionFunc = dedicatedConnectionFunc ?? throw new ArgumentNullException(nameof(dedicatedConnectionFunc));\n    }\n\n    public override void Commit()\n    {\n      _storage.UseTransaction(_dedicatedConnectionFunc(), (connection, _) => {\n        RegisterNewJobsEventWithTransactionCompletedEvent();\n        foreach (Action<IDbConnection> command in _commandQueue)\n        {\n          command(connection);\n        }\n      }, CreateTransactionScope);\n    }\n\n    private void RegisterNewJobsEventWithTransactionCompletedEvent()\n    {\n      // TransactionCompleted event is required here, because if this TransactionScope is enlisted\n      // within an ambient TransactionScope, the ambient TransactionScope controls when the TransactionScope completes.\n      Transaction.Current.TransactionCompleted += (_, args) => {\n        if (args.Transaction.TransactionInformation.Status == TransactionStatus.Committed)\n        {\n          // Triggers signals for all queues to which jobs have been added in this transaction\n          _queuesWithAddedJobs.ForEach(PostgreSqlJobQueue._queueEventRegistry.Set);\n          _queuesWithAddedJobs.Clear();\n        }\n      };\n    }\n\n    private TransactionScope CreateTransactionScope()\n    {\n      return _storage.CreateTransactionScope(null, TransactionManager.MaximumTimeout);\n    }\n\n    public override void ExpireJob(string jobId, TimeSpan expireIn)\n    {\n      string sql = $@\"\n        UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\"\n        SET \"\"expireat\"\" = NOW() + INTERVAL '{(long)expireIn.TotalSeconds} SECONDS'\n        WHERE \"\"id\"\" = @Id;\n      \";\n\n      QueueCommand(con => con.Execute(sql,\n        new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture) }));\n    }\n\n    public override void PersistJob(string jobId)\n    {\n      string sql = $@\"\n        UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" \n        SET \"\"expireat\"\" = NULL \n        WHERE \"\"id\"\" = @Id;\n      \";\n\n      QueueCommand(con => con.Execute(sql,\n        new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture) }));\n    }\n\n    public override void SetJobState(string jobId, IState state)\n    {\n      string addAndSetStateSql = $@\"\n        WITH \"\"s\"\" AS (\n            INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"state\"\" (\"\"jobid\"\", \"\"name\"\", \"\"reason\"\", \"\"createdat\"\", \"\"data\"\")\n            VALUES (@JobId, @Name, @Reason, @CreatedAt, @Data::jsonb) RETURNING \"\"id\"\"\n        )\n        UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"job\"\" \"\"j\"\"\n        SET \"\"stateid\"\" = s.\"\"id\"\", \"\"statename\"\" = @Name\n        FROM \"\"s\"\"\n        WHERE \"\"j\"\".\"\"id\"\" = @Id;\n      \";\n\n      QueueCommand(con => con.Execute(addAndSetStateSql,\n        new {\n          JobId = Convert.ToInt64(jobId, CultureInfo.InvariantCulture),\n          state.Name,\n          state.Reason,\n          CreatedAt = DateTime.UtcNow,\n          Data = JsonParameter.GetParameterValue(SerializationHelper.Serialize(state.SerializeData())),\n          Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture),\n        }));\n    }\n\n    public override void AddJobState(string jobId, IState state)\n    {\n      string addStateSql = $@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"state\"\" (\"\"jobid\"\", \"\"name\"\", \"\"reason\"\", \"\"createdat\"\", \"\"data\"\")\n        VALUES (@JobId, @Name, @Reason, @CreatedAt, @Data::jsonb);\n      \";\n\n      QueueCommand(con => con.Execute(addStateSql,\n        new {\n          JobId = Convert.ToInt64(jobId, CultureInfo.InvariantCulture),\n          state.Name,\n          state.Reason,\n          CreatedAt = DateTime.UtcNow,\n          Data = JsonParameter.GetParameterValue(SerializationHelper.Serialize(state.SerializeData())),\n        }));\n    }\n\n    public override void AddToQueue(string queue, string jobId)\n    {\n      IPersistentJobQueueProvider provider = _storage.QueueProviders.GetProvider(queue);\n      IPersistentJobQueue persistentQueue = provider.GetJobQueue();\n\n      QueueCommand(con => persistentQueue.Enqueue(con, queue, jobId));\n      \n      _queuesWithAddedJobs.Add(queue);\n    }\n\n    public override void IncrementCounter(string key)\n    {\n      string sql = $@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\" (\"\"key\"\", \"\"value\"\") \n        VALUES (@Key, @Value);\n      \";\n      QueueCommand(con => con.Execute(sql,\n        new { Key = key, Value = +1 }));\n    }\n\n    public override void IncrementCounter(string key, TimeSpan expireIn)\n    {\n      string sql = $@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\"(\"\"key\"\", \"\"value\"\", \"\"expireat\"\") \n        VALUES (@Key, @Value, NOW() + INTERVAL '{(long)expireIn.TotalSeconds} SECONDS');\n      \";\n      QueueCommand(con => con.Execute(sql,\n        new { Key = key, Value = +1 }));\n    }\n\n    public override void DecrementCounter(string key)\n    {\n      string sql = $@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\" (\"\"key\"\", \"\"value\"\") \n        VALUES (@Key, @Value);\n      \";\n      QueueCommand(con => con.Execute(sql,\n        new { Key = key, Value = -1 }));\n    }\n\n    public override void DecrementCounter(string key, TimeSpan expireIn)\n    {\n      string sql = $@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"counter\"\"(\"\"key\"\", \"\"value\"\", \"\"expireat\"\") \n        VALUES (@Key, @Value, NOW() + INTERVAL '{((long)expireIn.TotalSeconds).ToString(CultureInfo.InvariantCulture)} SECONDS');\n      \";\n      QueueCommand(con => con.Execute(sql, new { Key = key, Value = -1 }));\n    }\n\n    public override void AddToSet(string key, string value)\n    {\n      AddToSet(key, value, 0.0);\n    }\n\n    public override void AddToSet(string key, string value, double score)\n    {\n      string addSql = $@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"set\"\"(\"\"key\"\", \"\"value\"\", \"\"score\"\")\n        VALUES(@Key, @Value, @Score)\n        ON CONFLICT (\"\"key\"\", \"\"value\"\")\n        DO UPDATE SET \"\"score\"\" = EXCLUDED.\"\"score\"\"\n      \";\n      QueueCommand(con => con.Execute(addSql,\n        new { Key = key, Value = value, Score = score }));\n    }\n\n    public override void RemoveFromSet(string key, string value)\n    {\n      QueueCommand(con => con.Execute($@\"\n        DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"set\"\" \n        WHERE \"\"key\"\" = @Key \n        AND \"\"value\"\" = @Value;\n      \",\n      new { Key = key, Value = value }));\n    }\n\n    public override void InsertToList(string key, string value)\n    {\n      QueueCommand(con => con.Execute($@\"\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"list\"\" (\"\"key\"\", \"\"value\"\") \n        VALUES (@Key, @Value);\n      \",\n      new { Key = key, Value = value }));\n    }\n\n    public override void RemoveFromList(string key, string value)\n    {\n      QueueCommand(con => con.Execute($@\"\n        DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"list\"\" \n        WHERE \"\"key\"\" = @Key \n        AND \"\"value\"\" = @Value;\n      \", new { Key = key, Value = value }));\n    }\n\n    public override void TrimList(string key, int keepStartingFrom, int keepEndingAt)\n    {\n      string trimSql = $@\"\n        DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"list\"\" AS source\n        WHERE \"\"key\"\" = @Key\n        AND \"\"id\"\" NOT IN (\n            SELECT \"\"id\"\" \n            FROM \"\"{_storage.Options.SchemaName}\"\".\"\"list\"\" AS keep\n            WHERE keep.\"\"key\"\" = source.\"\"key\"\"\n            ORDER BY \"\"id\"\" \n            OFFSET @Offset LIMIT @Limit\n        );\n      \";\n\n      QueueCommand(con => con.Execute(trimSql,\n        new { Key = key, Offset = keepStartingFrom, Limit = keepEndingAt - keepStartingFrom + 1 }));\n    }\n\n    public override void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      if (keyValuePairs == null)\n      {\n        throw new ArgumentNullException(nameof(keyValuePairs));\n      }\n\n      string sql = $@\"\n        WITH \"\"inputvalues\"\" AS (\n\t        SELECT @Key \"\"key\"\", @Field \"\"field\"\", @Value \"\"value\"\"\n        ), \"\"updatedrows\"\" AS ( \n\t        UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"hash\"\" \"\"updatetarget\"\"\n\t        SET \"\"value\"\" = \"\"inputvalues\"\".\"\"value\"\"\n\t        FROM \"\"inputvalues\"\"\n\t        WHERE \"\"updatetarget\"\".\"\"key\"\" = \"\"inputvalues\"\".\"\"key\"\"\n\t        AND \"\"updatetarget\"\".\"\"field\"\" = \"\"inputvalues\"\".\"\"field\"\"\n\t        RETURNING \"\"updatetarget\"\".\"\"key\"\", \"\"updatetarget\"\".\"\"field\"\"\n        )\n        INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"hash\"\"(\"\"key\"\", \"\"field\"\", \"\"value\"\")\n        SELECT \"\"key\"\", \"\"field\"\", \"\"value\"\" \n        FROM \"\"inputvalues\"\" \"\"insertvalues\"\"\n        WHERE NOT EXISTS (\n\t        SELECT 1 \n\t        FROM \"\"updatedrows\"\" \n\t        WHERE \"\"updatedrows\"\".\"\"key\"\" = \"\"insertvalues\"\".\"\"key\"\" \n\t        AND \"\"updatedrows\"\".\"\"field\"\" = \"\"insertvalues\"\".\"\"field\"\"\n        );\n      \";\n      foreach (KeyValuePair<string, string> keyValuePair in keyValuePairs)\n      {\n        KeyValuePair<string, string> pair = keyValuePair;\n        QueueCommand(con => con.Execute(sql, new { Key = key, Field = pair.Key, pair.Value }));\n      }\n    }\n\n    public override void RemoveHash(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"hash\"\" WHERE \"\"key\"\" = @Key\";\n      QueueCommand(con => con.Execute(sql,\n        new { Key = key }));\n    }\n\n    public override void ExpireSet(string key, TimeSpan expireIn)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"set\"\" SET \"\"expireat\"\" = @ExpireAt WHERE \"\"key\"\" = @Key\";\n\n      QueueCommand(connection => connection.Execute(sql,\n        new { Key = key, ExpireAt = DateTime.UtcNow.Add(expireIn) }));\n    }\n\n    public override void ExpireList(string key, TimeSpan expireIn)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"list\"\" SET \"\"expireat\"\" = @ExpireAt WHERE \"\"key\"\" = @Key\";\n\n      QueueCommand(connection => connection.Execute(sql,\n        new { Key = key, ExpireAt = DateTime.UtcNow.Add(expireIn) }));\n    }\n\n    public override void ExpireHash(string key, TimeSpan expireIn)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"hash\"\" SET expireat = @ExpireAt WHERE \"\"key\"\" = @Key\";\n\n      QueueCommand(connection => connection.Execute(sql,\n        new { Key = key, ExpireAt = DateTime.UtcNow.Add(expireIn) }));\n    }\n\n    public override void PersistSet(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"set\"\" SET expireat = null WHERE \"\"key\"\" = @Key\";\n\n      QueueCommand(connection => connection.Execute(sql, new { Key = key }));\n    }\n\n    public override void PersistList(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"list\"\" SET expireat = null WHERE \"\"key\"\" = @Key\";\n\n      QueueCommand(connection => connection.Execute(sql, new { Key = key }));\n    }\n\n    public override void PersistHash(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"UPDATE \"\"{_storage.Options.SchemaName}\"\".\"\"hash\"\" SET expireat = null WHERE \"\"key\"\" = @Key\";\n\n      QueueCommand(connection => connection.Execute(sql,\n        new { Key = key }));\n    }\n\n    public override void AddRangeToSet(string key, IList<string> items)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      if (items == null)\n      {\n        throw new ArgumentNullException(nameof(items));\n      }\n\n      string sql = $@\"INSERT INTO \"\"{_storage.Options.SchemaName}\"\".\"\"set\"\" (\"\"key\"\", \"\"value\"\", \"\"score\"\") VALUES (@Key, @Value, 0.0)\";\n\n      QueueCommand(connection => connection.Execute(sql,\n        items.Select(value => new { Key = key, Value = value }).ToList()));\n    }\n\n    public override void RemoveSet(string key)\n    {\n      if (key == null)\n      {\n        throw new ArgumentNullException(nameof(key));\n      }\n\n      string sql = $@\"DELETE FROM \"\"{_storage.Options.SchemaName}\"\".\"\"set\"\" WHERE \"\"key\"\" = @Key\";\n\n      QueueCommand(connection => connection.Execute(sql, new { Key = key }));\n    }\n\n    internal void QueueCommand(Action<IDbConnection> action)\n    {\n      _commandQueue.Enqueue(action);\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Properties/Annotations.cs",
    "content": "﻿using System;\n\n#pragma warning disable 1591\n// ReSharper disable UnusedMember.Global\n// ReSharper disable UnusedParameter.Local\n// ReSharper disable MemberCanBePrivate.Global\n// ReSharper disable UnusedAutoPropertyAccessor.Global\n// ReSharper disable IntroduceOptionalParameters.Global\n// ReSharper disable MemberCanBeProtected.Global\n// ReSharper disable InconsistentNaming\n\nnamespace Hangfire.PostgreSql.Properties\n{\n  /// <summary>\n  ///   Indicates that the value of the marked element could be <c>null</c> sometimes,\n  ///   so the check for <c>null</c> is necessary before its usage\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [CanBeNull] public object Test() { return null; }\n  /// public void UseTest() {\n  ///   var p = Test();\n  ///   var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field)]\n  public sealed class CanBeNullAttribute : Attribute { }\n\n  /// <summary>\n  ///   Indicates that the value of the marked element could never be <c>null</c>\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [NotNull] public object Foo() {\n  ///   return null; // Warning: Possible 'null' assignment\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field)]\n  public sealed class NotNullAttribute : Attribute { }\n\n  /// <summary>\n  ///   Indicates that the marked method builds string by format pattern and (optional) arguments.\n  ///   Parameter, which contains format string, should be given in constructor. The format string\n  ///   should be in <see cref=\"string.Format(IFormatProvider,string,object[])\" />-like form\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [StringFormatMethod(\"message\")]\n  /// public void ShowError(string message, params object[] args) { /* do something */ }\n  /// public void Foo() {\n  ///   ShowError(\"Failed: {0}\"); // Warning: Non-existing argument in format string\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method)]\n  public sealed class StringFormatMethodAttribute : Attribute\n  {\n    /// <param name=\"formatParameterName\">\n    ///   Specifies which parameter of an annotated method should be treated as format-string\n    /// </param>\n    public StringFormatMethodAttribute(string formatParameterName)\n    {\n      FormatParameterName = formatParameterName;\n    }\n\n    public string FormatParameterName { get; }\n  }\n\n  /// <summary>\n  ///   Indicates that the function argument should be string literal and match one\n  ///   of the parameters of the caller function. For example, ReSharper annotates\n  ///   the parameter of <see cref=\"System.ArgumentNullException\" />\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// public void Foo(string param) {\n  ///   if (param == null)\n  ///     throw new ArgumentNullException(\"par\"); // Warning: Cannot resolve symbol\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class InvokerParameterNameAttribute : Attribute { }\n\n  /// <summary>\n  ///   Indicates that the method is contained in a type that implements\n  ///   <see cref=\"System.ComponentModel.INotifyPropertyChanged\" /> interface\n  ///   and this method is used to notify that some property value changed\n  /// </summary>\n  /// <remarks>\n  ///   The method should be non-static and conform to one of the supported signatures:\n  ///   <list>\n  ///     <item>\n  ///       <c>NotifyChanged(string)</c>\n  ///     </item>\n  ///     <item>\n  ///       <c>NotifyChanged(params string[])</c>\n  ///     </item>\n  ///     <item>\n  ///       <c>NotifyChanged{T}(Expression{Func{T}})</c>\n  ///     </item>\n  ///     <item>\n  ///       <c>NotifyChanged{T,U}(Expression{Func{T,U}})</c>\n  ///     </item>\n  ///     <item>\n  ///       <c>SetProperty{T}(ref T, T, string)</c>\n  ///     </item>\n  ///   </list>\n  /// </remarks>\n  /// <example>\n  ///   <code>\n  /// public class Foo : INotifyPropertyChanged {\n  ///   public event PropertyChangedEventHandler PropertyChanged;\n  ///   [NotifyPropertyChangedInvocator]\n  ///   protected virtual void NotifyChanged(string propertyName) { ... }\n  /// \n  ///   private string _name;\n  ///   public string Name {\n  ///     get { return _name; }\n  ///     set { _name = value; NotifyChanged(\"LastName\"); /* Warning */ }\n  ///   }\n  /// }\n  /// </code>\n  ///   Examples of generated notifications:\n  ///   <list>\n  ///     <item>\n  ///       <c>NotifyChanged(\"Property\")</c>\n  ///     </item>\n  ///     <item>\n  ///       <c>NotifyChanged(() =&gt; Property)</c>\n  ///     </item>\n  ///     <item>\n  ///       <c>NotifyChanged((VM x) =&gt; x.Property)</c>\n  ///     </item>\n  ///     <item>\n  ///       <c>SetProperty(ref myField, value, \"Property\")</c>\n  ///     </item>\n  ///   </list>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute\n  {\n    public NotifyPropertyChangedInvocatorAttribute() { }\n\n    public NotifyPropertyChangedInvocatorAttribute(string parameterName)\n    {\n      ParameterName = parameterName;\n    }\n\n    public string ParameterName { get; }\n  }\n\n  /// <summary>\n  ///   Describes dependency between method input and output\n  /// </summary>\n  /// <syntax>\n  ///   <p>Function Definition Table syntax:</p>\n  ///   <list>\n  ///     <item>FDT      ::= FDTRow [;FDTRow]*</item>\n  ///     <item>FDTRow   ::= Input =&gt; Output | Output &lt;= Input</item>\n  ///     <item>Input    ::= ParameterName: Value [, Input]*</item>\n  ///     <item>Output   ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>\n  ///     <item>Value    ::= true | false | null | notnull | canbenull</item>\n  ///   </list>\n  ///   If method has single input parameter, it's name could be omitted.<br />\n  ///   Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same)\n  ///   for method output means that the methos doesn't return normally.<br />\n  ///   <c>canbenull</c> annotation is only applicable for output parameters.<br />\n  ///   You can use multiple <c>[ContractAnnotation]</c> for each FDT row,\n  ///   or use single attribute with rows separated by semicolon.<br />\n  /// </syntax>\n  /// <examples>\n  ///   <list>\n  ///     <item>\n  ///       <code>\n  /// [ContractAnnotation(\"=> halt\")]\n  /// public void TerminationMethod()\n  /// </code>\n  ///     </item>\n  ///     <item>\n  ///       <code>\n  /// [ContractAnnotation(\"halt &lt;= condition: false\")]\n  /// public void Assert(bool condition, string text) // regular assertion method\n  /// </code>\n  ///     </item>\n  ///     <item>\n  ///       <code>\n  /// [ContractAnnotation(\"s:null => true\")]\n  /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()\n  /// </code>\n  ///     </item>\n  ///     <item>\n  ///       <code>\n  /// // A method that returns null if the parameter is null, and not null if the parameter is not null\n  /// [ContractAnnotation(\"null => null; notnull => notnull\")]\n  /// public object Transform(object data) \n  /// </code>\n  ///     </item>\n  ///     <item>\n  ///       <code>\n  /// [ContractAnnotation(\"s:null=>false; =>true,result:notnull; =>false, result:null\")]\n  /// public bool TryParse(string s, out Person result)\n  /// </code>\n  ///     </item>\n  ///   </list>\n  /// </examples>\n  [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]\n  public sealed class ContractAnnotationAttribute : Attribute\n  {\n    public ContractAnnotationAttribute([NotNull] string contract)\n      : this(contract, false) { }\n\n    public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)\n    {\n      Contract = contract;\n      ForceFullStates = forceFullStates;\n    }\n\n    public string Contract { get; }\n    public bool ForceFullStates { get; }\n  }\n\n  /// <summary>\n  ///   Indicates that marked element should be localized or not\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [LocalizationRequiredAttribute(true)]\n  /// public class Foo {\n  ///   private string str = \"my string\"; // Warning: Localizable string\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.All)]\n  public sealed class LocalizationRequiredAttribute : Attribute\n  {\n    public LocalizationRequiredAttribute() : this(true) { }\n\n    public LocalizationRequiredAttribute(bool required)\n    {\n      Required = required;\n    }\n\n    public bool Required { get; }\n  }\n\n  /// <summary>\n  ///   Indicates that the value of the marked type (or its derivatives)\n  ///   cannot be compared using '==' or '!=' operators and <c>Equals()</c>\n  ///   should be used instead. However, using '==' or '!=' for comparison\n  ///   with <c>null</c> is always permitted.\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [CannotApplyEqualityOperator]\n  /// class NoEquality { }\n  /// class UsesNoEquality {\n  ///   public void Test() {\n  ///     var ca1 = new NoEquality();\n  ///     var ca2 = new NoEquality();\n  ///     if (ca1 != null) { // OK\n  ///       bool condition = ca1 == ca2; // Warning\n  ///     }\n  ///   }\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]\n  public sealed class CannotApplyEqualityOperatorAttribute : Attribute { }\n\n  /// <summary>\n  ///   When applied to a target attribute, specifies a requirement for any type marked\n  ///   with the target attribute to implement or inherit specific type or types.\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement\n  /// public class ComponentAttribute : Attribute { }\n  /// [Component] // ComponentAttribute requires implementing IComponent interface\n  /// public class MyComponent : IComponent { }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n  [BaseTypeRequired(typeof(Attribute))]\n  public sealed class BaseTypeRequiredAttribute : Attribute\n  {\n    public BaseTypeRequiredAttribute([NotNull] Type baseType)\n    {\n      BaseType = baseType;\n    }\n\n    [NotNull] public Type BaseType { get; }\n  }\n\n  /// <summary>\n  ///   Indicates that the marked symbol is used implicitly\n  ///   (e.g. via reflection, in external library), so this symbol\n  ///   will not be marked as unused (as well as by other usage inspections)\n  /// </summary>\n  [AttributeUsage(AttributeTargets.All)]\n  [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)]\n  public sealed class UsedImplicitlyAttribute : Attribute\n  {\n    public UsedImplicitlyAttribute()\n      : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }\n\n    public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)\n      : this(useKindFlags, ImplicitUseTargetFlags.Default) { }\n\n    public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)\n      : this(ImplicitUseKindFlags.Default, targetFlags) { }\n\n    public UsedImplicitlyAttribute(\n      ImplicitUseKindFlags useKindFlags,\n      ImplicitUseTargetFlags targetFlags)\n    {\n      UseKindFlags = useKindFlags;\n      TargetFlags = targetFlags;\n    }\n\n    public ImplicitUseKindFlags UseKindFlags { get; }\n    public ImplicitUseTargetFlags TargetFlags { get; }\n  }\n\n  /// <summary>\n  ///   Should be used on attributes and causes ReSharper\n  ///   to not mark symbols marked with such attributes as unused\n  ///   (as well as by other usage inspections)\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Class)]\n  public sealed class MeansImplicitUseAttribute : Attribute\n  {\n    public MeansImplicitUseAttribute()\n      : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }\n\n    public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)\n      : this(useKindFlags, ImplicitUseTargetFlags.Default) { }\n\n    public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)\n      : this(ImplicitUseKindFlags.Default, targetFlags) { }\n\n    public MeansImplicitUseAttribute(\n      ImplicitUseKindFlags useKindFlags,\n      ImplicitUseTargetFlags targetFlags)\n    {\n      UseKindFlags = useKindFlags;\n      TargetFlags = targetFlags;\n    }\n\n    [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; }\n    [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; }\n  }\n\n  [Flags]\n  public enum ImplicitUseKindFlags\n  {\n    Default = Access | Assign | InstantiatedWithFixedConstructorSignature,\n\n    /// <summary>Only entity marked with attribute considered used</summary>\n    Access = 1,\n\n    /// <summary>Indicates implicit assignment to a member</summary>\n    Assign = 2,\n\n    /// <summary>\n    ///   Indicates implicit instantiation of a type with fixed constructor signature.\n    ///   That means any unused constructor parameters won't be reported as such.\n    /// </summary>\n    InstantiatedWithFixedConstructorSignature = 4,\n\n    /// <summary>Indicates implicit instantiation of a type</summary>\n    InstantiatedNoFixedConstructorSignature = 8,\n  }\n\n  /// <summary>\n  ///   Specify what is considered used implicitly\n  ///   when marked with <see cref=\"MeansImplicitUseAttribute\" />\n  ///   or <see cref=\"UsedImplicitlyAttribute\" />\n  /// </summary>\n  [Flags]\n  public enum ImplicitUseTargetFlags\n  {\n    Default = Itself,\n    Itself = 1,\n\n    /// <summary>Members of entity marked with attribute are considered used</summary>\n    Members = 2,\n\n    /// <summary>Entity marked with attribute and all its members considered used</summary>\n    WithMembers = Itself | Members,\n  }\n\n  /// <summary>\n  ///   This attribute is intended to mark publicly available API\n  ///   which should not be removed and so is treated as used\n  /// </summary>\n  [MeansImplicitUse]\n  public sealed class PublicAPIAttribute : Attribute\n  {\n    public PublicAPIAttribute() { }\n\n    public PublicAPIAttribute([NotNull] string comment)\n    {\n      Comment = comment;\n    }\n\n    [NotNull] public string Comment { get; }\n  }\n\n  /// <summary>\n  ///   Tells code analysis engine if the parameter is completely handled\n  ///   when the invoked method is on stack. If the parameter is a delegate,\n  ///   indicates that delegate is executed while the method is executed.\n  ///   If the parameter is an enumerable, indicates that it is enumerated\n  ///   while the method is executed\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class InstantHandleAttribute : Attribute { }\n\n  /// <summary>\n  ///   Indicates that a method does not make any observable state changes.\n  ///   The same as <c>System.Diagnostics.Contracts.PureAttribute</c>\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [Pure] private int Multiply(int x, int y) { return x * y; }\n  /// public void Foo() {\n  ///   const int a = 2, b = 2;\n  ///   Multiply(a, b); // Waring: Return value of pure method is not used\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class PureAttribute : Attribute { }\n\n  /// <summary>\n  ///   Indicates that a parameter is a path to a file or a folder\n  ///   within a web project. Path can be relative or absolute,\n  ///   starting from web root (~)\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public class PathReferenceAttribute : Attribute\n  {\n    public PathReferenceAttribute() { }\n\n    public PathReferenceAttribute([PathReference] string basePath)\n    {\n      BasePath = basePath;\n    }\n\n    [NotNull] public string BasePath { get; }\n  }\n\n  // ASP.NET MVC attributes\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute\n  {\n    public AspMvcAreaMasterLocationFormatAttribute(string format) { }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcAreaPartialViewLocationFormatAttribute(string format) { }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcAreaViewLocationFormatAttribute(string format) { }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class AspMvcMasterLocationFormatAttribute : Attribute\n  {\n    public AspMvcMasterLocationFormatAttribute(string format) { }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcPartialViewLocationFormatAttribute(string format) { }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class AspMvcViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcViewLocationFormatAttribute(string format) { }\n  }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter\n  ///   is an MVC action. If applied to a method, the MVC action name is calculated\n  ///   implicitly from the context. Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]\n  public sealed class AspMvcActionAttribute : Attribute\n  {\n    public AspMvcActionAttribute() { }\n\n    public AspMvcActionAttribute([NotNull] string anonymousProperty)\n    {\n      AnonymousProperty = anonymousProperty;\n    }\n\n    [NotNull] public string AnonymousProperty { get; }\n  }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. Indicates that a parameter is an MVC area.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AspMvcAreaAttribute : PathReferenceAttribute\n  {\n    public AspMvcAreaAttribute() { }\n\n    public AspMvcAreaAttribute([NotNull] string anonymousProperty)\n    {\n      AnonymousProperty = anonymousProperty;\n    }\n\n    [NotNull] public string AnonymousProperty { get; }\n  }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. If applied to a parameter, indicates that\n  ///   the parameter is an MVC controller. If applied to a method,\n  ///   the MVC controller name is calculated implicitly from the context.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]\n  public sealed class AspMvcControllerAttribute : Attribute\n  {\n    public AspMvcControllerAttribute() { }\n\n    public AspMvcControllerAttribute([NotNull] string anonymousProperty)\n    {\n      AnonymousProperty = anonymousProperty;\n    }\n\n    [NotNull] public string AnonymousProperty { get; }\n  }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. Indicates that a parameter is an MVC Master.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Controller.View(String, String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AspMvcMasterAttribute : Attribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. Indicates that a parameter is an MVC model type.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Controller.View(String, Object)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AspMvcModelTypeAttribute : Attribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. If applied to a parameter, indicates that\n  ///   the parameter is an MVC partial view. If applied to a method,\n  ///   the MVC partial view name is calculated implicitly from the context.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]\n  public sealed class AspMvcPartialViewAttribute : PathReferenceAttribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. Allows disabling all inspections\n  ///   for MVC views within a class or a method.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]\n  public sealed class AspMvcSupressViewErrorAttribute : Attribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. Indicates that a parameter is an MVC display template.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AspMvcDisplayTemplateAttribute : Attribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AspMvcEditorTemplateAttribute : Attribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. Indicates that a parameter is an MVC template.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.ComponentModel.DataAnnotations.UIHintAttribute(System.String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AspMvcTemplateAttribute : Attribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter\n  ///   is an MVC view. If applied to a method, the MVC view name is calculated implicitly\n  ///   from the context. Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.Mvc.Controller.View(Object)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]\n  public sealed class AspMvcViewAttribute : PathReferenceAttribute { }\n\n  /// <summary>\n  ///   ASP.NET MVC attribute. When applied to a parameter of an attribute,\n  ///   indicates that this parameter is an MVC action name\n  /// </summary>\n  /// <example>\n  ///   <code>\n  /// [ActionName(\"Foo\")]\n  /// public ActionResult Login(string returnUrl) {\n  ///   ViewBag.ReturnUrl = Url.Action(\"Foo\"); // OK\n  ///   return RedirectToAction(\"Bar\"); // Error: Cannot resolve action\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]\n  public sealed class AspMvcActionSelectorAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)]\n  public sealed class HtmlElementAttributesAttribute : Attribute\n  {\n    public HtmlElementAttributesAttribute() { }\n\n    public HtmlElementAttributesAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [NotNull] public string Name { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class HtmlAttributeValueAttribute : Attribute\n  {\n    public HtmlAttributeValueAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [NotNull] public string Name { get; }\n  }\n\n  // Razor attributes\n\n  /// <summary>\n  ///   Razor attribute. Indicates that a parameter or a method is a Razor section.\n  ///   Use this attribute for custom wrappers similar to\n  ///   <c>System.Web.WebPages.WebPageBase.RenderSection(String)</c>\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]\n  public sealed class RazorSectionAttribute : Attribute { }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"Hangfire.PostgreSql.Tests\")]\n// Allow the generation of mocks for internal types\n[assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2\")]\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v10.sql",
    "content": "SET search_path = 'hangfire';\n--\n-- Table structure for table `Schema`\n--\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 10) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"jobqueue\"\n    ALTER COLUMN \"queue\" TYPE TEXT;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v11.sql",
    "content": "SET search_path = 'hangfire';\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 11) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"counter\"\n    ALTER COLUMN id TYPE BIGINT;\nALTER TABLE \"hash\"\n    ALTER COLUMN id TYPE BIGINT;\nALTER TABLE \"job\"\n    ALTER COLUMN id TYPE BIGINT;\nALTER TABLE \"job\"\n    ALTER COLUMN stateid TYPE BIGINT;\nALTER TABLE \"state\"\n    ALTER COLUMN id TYPE BIGINT;\nALTER TABLE \"state\"\n    ALTER COLUMN jobid TYPE BIGINT;\nALTER TABLE \"jobparameter\"\n    ALTER COLUMN id TYPE BIGINT;\nALTER TABLE \"jobparameter\"\n    ALTER COLUMN jobid TYPE BIGINT;\nALTER TABLE \"jobqueue\"\n    ALTER COLUMN id TYPE BIGINT;\nALTER TABLE \"jobqueue\"\n    ALTER COLUMN jobid TYPE BIGINT;\nALTER TABLE \"list\"\n    ALTER COLUMN id TYPE BIGINT;\nALTER TABLE \"set\"\n    ALTER COLUMN id TYPE BIGINT;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v12.sql",
    "content": "SET search_path = 'hangfire';\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 12) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"counter\"\n    ALTER COLUMN \"key\" TYPE TEXT;\nALTER TABLE \"hash\"\n    ALTER COLUMN \"key\" TYPE TEXT;\nALTER TABLE \"hash\"\n    ALTER COLUMN field TYPE TEXT;\nALTER TABLE \"job\"\n    ALTER COLUMN statename TYPE TEXT;\nALTER TABLE \"list\"\n    ALTER COLUMN \"key\" TYPE TEXT;\nALTER TABLE \"server\"\n    ALTER COLUMN id TYPE TEXT;\nALTER TABLE \"set\"\n    ALTER COLUMN \"key\" TYPE TEXT;\nALTER TABLE \"jobparameter\"\n    ALTER COLUMN \"name\" TYPE TEXT;\nALTER TABLE \"state\"\n    ALTER COLUMN \"name\" TYPE TEXT;\nALTER TABLE \"state\"\n    ALTER COLUMN reason TYPE TEXT;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v13.sql",
    "content": "SET search_path = 'hangfire';\n\n\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 13) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nCREATE INDEX IF NOT EXISTS jobqueue_queue_fetchat_jobId ON jobqueue USING btree (queue asc, fetchedat asc nulls last, jobid asc);\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v14.sql",
    "content": "SET search_path = 'hangfire';\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 14) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\ndo\n$$\n    DECLARE\n    BEGIN\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".job_id_seq AS bigint MAXVALUE 9223372036854775807');\n    EXCEPTION\n        WHEN syntax_error THEN\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".job_id_seq MAXVALUE 9223372036854775807');\n    END;\n$$;\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v15.sql",
    "content": "SET search_path = 'hangfire';\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 15) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nCREATE INDEX ix_hangfire_job_expireat ON \"job\" (expireat);\nCREATE INDEX ix_hangfire_list_expireat ON \"list\" (expireat);\nCREATE INDEX ix_hangfire_set_expireat ON \"set\" (expireat);\nCREATE INDEX ix_hangfire_hash_expireat ON \"hash\" (expireat);\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v16.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 16) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\n-- Note: job_id_seq is already bigint as per migration script v14\nDO\n$$\n    DECLARE\n    BEGIN\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".counter_id_seq AS bigint MAXVALUE 9223372036854775807');\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".hash_id_seq AS bigint MAXVALUE 9223372036854775807');\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".jobparameter_id_seq AS bigint MAXVALUE 9223372036854775807');\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".jobqueue_id_seq AS bigint MAXVALUE 9223372036854775807');\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".list_id_seq AS bigint MAXVALUE 9223372036854775807');\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".set_id_seq AS bigint MAXVALUE 9223372036854775807');\n        EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".state_id_seq AS bigint MAXVALUE 9223372036854775807');\n    EXCEPTION\n        WHEN syntax_error THEN\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".counter_id_seq MAXVALUE 9223372036854775807');\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".hash_id_seq MAXVALUE 9223372036854775807');\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".jobparameter_id_seq MAXVALUE 9223372036854775807');\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".jobqueue_id_seq MAXVALUE 9223372036854775807');\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".list_id_seq MAXVALUE 9223372036854775807');\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".set_id_seq MAXVALUE 9223372036854775807');\n            EXECUTE ('ALTER SEQUENCE \"' || 'hangfire' || '\".state_id_seq MAXVALUE 9223372036854775807');\n    END\n$$;\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v17.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 17) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nCREATE INDEX IF NOT EXISTS ix_hangfire_set_key_score ON \"set\" (key, score);\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v18.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 18) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nCREATE TABLE aggregatedcounter (\n    \"id\" bigserial PRIMARY KEY NOT NULL,\n    \"key\" text NOT NULL UNIQUE,\n    \"value\" int8 NOT NULL,\n    \"expireat\" timestamp\n);\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v19.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDO $$\nBEGIN\n    IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 19) THEN\n        RAISE EXCEPTION 'version-already-applied';\nEND IF;\nEND $$;\n\nALTER TABLE \"aggregatedcounter\" ALTER COLUMN \"expireat\" TYPE timestamp with time zone;\nALTER TABLE \"counter\" ALTER COLUMN \"expireat\" TYPE timestamp with time zone;\nALTER TABLE \"hash\" ALTER COLUMN \"expireat\" TYPE timestamp with time zone;\nALTER TABLE \"job\" ALTER COLUMN \"createdat\" TYPE timestamp with time zone;\nALTER TABLE \"job\" ALTER COLUMN \"expireat\" TYPE timestamp with time zone;\nALTER TABLE \"jobqueue\" ALTER COLUMN \"fetchedat\" TYPE timestamp with time zone;\nALTER TABLE \"list\" ALTER COLUMN \"expireat\" TYPE timestamp with time zone;\nALTER TABLE \"lock\" ALTER COLUMN \"acquired\" TYPE timestamp with time zone;\nALTER TABLE \"server\" ALTER COLUMN \"lastheartbeat\" TYPE timestamp with time zone;\nALTER TABLE \"set\" ALTER COLUMN \"expireat\" TYPE timestamp with time zone;\nALTER TABLE \"state\" ALTER COLUMN \"createdat\" TYPE timestamp with time zone;\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v20.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDO $$\nBEGIN\n    IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 20) THEN\n        RAISE EXCEPTION 'version-already-applied';\nEND IF;\nEND $$;\n\n-- Update existing jobs, if any have empty values first\nUPDATE \"job\" SET \"invocationdata\" = '{}' WHERE \"invocationdata\" = '';\nUPDATE \"job\" SET \"arguments\" = '[]' WHERE \"arguments\" = '';\n\n-- Change the type\n\nALTER TABLE \"job\" ALTER COLUMN \"invocationdata\" TYPE jsonb USING \"invocationdata\"::jsonb;\nALTER TABLE \"job\" ALTER COLUMN \"arguments\" TYPE jsonb USING \"arguments\"::jsonb;\nALTER TABLE \"server\" ALTER COLUMN \"data\" TYPE jsonb USING \"data\"::jsonb;\nALTER TABLE \"state\" ALTER COLUMN \"data\" TYPE jsonb USING \"data\"::jsonb;\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v21.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDO $$\nBEGIN\n    IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 21) THEN\n        RAISE EXCEPTION 'version-already-applied';\nEND IF;\nEND $$;\n\n-- Set REPLICA IDENTITY to allow replication\nALTER TABLE \"lock\" REPLICA IDENTITY USING INDEX \"lock_resource_key\";\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v22.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDO $$\nBEGIN\n    IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 22) THEN\n        RAISE EXCEPTION 'version-already-applied';\nEND IF;\nEND $$;\n\nDROP INDEX IF EXISTS jobqueue_queue_fetchat_jobId;\nCREATE INDEX IF NOT EXISTS ix_hangfire_jobqueue_fetchedat_queue_jobid ON jobqueue USING btree (fetchedat nulls first, queue, jobid);\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v23.sql",
    "content": "SET search_path = 'hangfire';\n\nDO $$\nBEGIN\n    IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 23) THEN\n        RAISE EXCEPTION 'version-already-applied';\n    END IF;\nEND $$;\n\nDROP INDEX IF EXISTS ix_hangfire_job_statename_is_not_null;\n\nDO $$\nBEGIN\n    IF current_setting('server_version_num')::int >= 110000 THEN\n        EXECUTE 'CREATE INDEX ix_hangfire_job_statename_is_not_null ON job USING btree(statename) INCLUDE (id) WHERE statename IS NOT NULL';\n    ELSE\n\t\tCREATE INDEX ix_hangfire_job_statename_is_not_null ON job USING btree(statename) WHERE statename IS NOT NULL;\n    END IF;\nEND $$;\n\nRESET search_path;\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v3.sql",
    "content": "﻿DO\n$$\n    BEGIN\n        IF NOT EXISTS(\n            SELECT schema_name\n            FROM information_schema.schemata\n            WHERE schema_name = 'hangfire'\n            )\n        THEN\n            EXECUTE 'CREATE SCHEMA \"hangfire\";';\n        END IF;\n\n    END\n$$;\n\nSET search_path = 'hangfire';\n--\n-- Table structure for table `Schema`\n--\n\nCREATE TABLE IF NOT EXISTS \"schema\"\n(\n    \"version\" INT NOT NULL,\n    PRIMARY KEY (\"version\")\n);\n\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 3) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nINSERT INTO \"schema\"(\"version\")\nVALUES ('1');\n\n--\n-- Table structure for table `Counter`\n--\n\nCREATE TABLE IF NOT EXISTS \"counter\"\n(\n    \"id\"       SERIAL       NOT NULL,\n    \"key\"      VARCHAR(100) NOT NULL,\n    \"value\"    SMALLINT     NOT NULL,\n    \"expireat\" TIMESTAMP    NULL,\n    PRIMARY KEY (\"id\")\n);\n\nDO\n$$\n    BEGIN\n        BEGIN\n            CREATE INDEX \"ix_hangfire_counter_key\" ON \"counter\" (\"key\");\n        EXCEPTION\n            WHEN duplicate_table THEN RAISE NOTICE 'INDEX ix_hangfire_counter_key already exists.';\n        END;\n    END;\n$$;\n\n--\n-- Table structure for table `Hash`\n--\n\nCREATE TABLE IF NOT EXISTS \"hash\"\n(\n    \"id\"       SERIAL       NOT NULL,\n    \"key\"      VARCHAR(100) NOT NULL,\n    \"field\"    VARCHAR(100) NOT NULL,\n    \"value\"    TEXT         NULL,\n    \"expireat\" TIMESTAMP    NULL,\n    PRIMARY KEY (\"id\"),\n    UNIQUE (\"key\", \"field\")\n);\n\n\n--\n-- Table structure for table `Job`\n--\n\nCREATE TABLE IF NOT EXISTS \"job\"\n(\n    \"id\"             SERIAL      NOT NULL,\n    \"stateid\"        INT         NULL,\n    \"statename\"      VARCHAR(20) NULL,\n    \"invocationdata\" TEXT        NOT NULL,\n    \"arguments\"      TEXT        NOT NULL,\n    \"createdat\"      TIMESTAMP   NOT NULL,\n    \"expireat\"       TIMESTAMP   NULL,\n    PRIMARY KEY (\"id\")\n);\n\nDO\n$$\n    BEGIN\n        BEGIN\n            CREATE INDEX \"ix_hangfire_job_statename\" ON \"job\" (\"statename\");\n        EXCEPTION\n            WHEN duplicate_table THEN RAISE NOTICE 'INDEX \"ix_hangfire_job_statename\" already exists.';\n        END;\n    END;\n$$;\n\n--\n-- Table structure for table `State`\n--\n\nCREATE TABLE IF NOT EXISTS \"state\"\n(\n    \"id\"        SERIAL       NOT NULL,\n    \"jobid\"     INT          NOT NULL,\n    \"name\"      VARCHAR(20)  NOT NULL,\n    \"reason\"    VARCHAR(100) NULL,\n    \"createdat\" TIMESTAMP    NOT NULL,\n    \"data\"      TEXT         NULL,\n    PRIMARY KEY (\"id\"),\n    FOREIGN KEY (\"jobid\") REFERENCES \"job\" (\"id\") ON UPDATE CASCADE ON DELETE CASCADE\n);\n\nDO\n$$\n    BEGIN\n        BEGIN\n            CREATE INDEX \"ix_hangfire_state_jobid\" ON \"state\" (\"jobid\");\n        EXCEPTION\n            WHEN duplicate_table THEN RAISE NOTICE 'INDEX \"ix_hangfire_state_jobid\" already exists.';\n        END;\n    END;\n$$;\n\n\n\n--\n-- Table structure for table `JobQueue`\n--\n\nCREATE TABLE IF NOT EXISTS \"jobqueue\"\n(\n    \"id\"        SERIAL      NOT NULL,\n    \"jobid\"     INT         NOT NULL,\n    \"queue\"     VARCHAR(20) NOT NULL,\n    \"fetchedat\" TIMESTAMP   NULL,\n    PRIMARY KEY (\"id\")\n);\n\nDO\n$$\n    BEGIN\n        BEGIN\n            CREATE INDEX \"ix_hangfire_jobqueue_queueandfetchedat\" ON \"jobqueue\" (\"queue\", \"fetchedat\");\n        EXCEPTION\n            WHEN duplicate_table THEN RAISE NOTICE 'INDEX \"ix_hangfire_jobqueue_queueandfetchedat\" already exists.';\n        END;\n    END;\n$$;\n\n\n--\n-- Table structure for table `List`\n--\n\nCREATE TABLE IF NOT EXISTS \"list\"\n(\n    \"id\"       SERIAL       NOT NULL,\n    \"key\"      VARCHAR(100) NOT NULL,\n    \"value\"    TEXT         NULL,\n    \"expireat\" TIMESTAMP    NULL,\n    PRIMARY KEY (\"id\")\n);\n\n\n--\n-- Table structure for table `Server`\n--\n\nCREATE TABLE IF NOT EXISTS \"server\"\n(\n    \"id\"            VARCHAR(50) NOT NULL,\n    \"data\"          TEXT        NULL,\n    \"lastheartbeat\" TIMESTAMP   NOT NULL,\n    PRIMARY KEY (\"id\")\n);\n\n\n--\n-- Table structure for table `Set`\n--\n\nCREATE TABLE IF NOT EXISTS \"set\"\n(\n    \"id\"       SERIAL       NOT NULL,\n    \"key\"      VARCHAR(100) NOT NULL,\n    \"score\"    FLOAT8       NOT NULL,\n    \"value\"    TEXT         NOT NULL,\n    \"expireat\" TIMESTAMP    NULL,\n    PRIMARY KEY (\"id\"),\n    UNIQUE (\"key\", \"value\")\n);\n\n\n--\n-- Table structure for table `JobParameter`\n--\n\nCREATE TABLE IF NOT EXISTS \"jobparameter\"\n(\n    \"id\"    SERIAL      NOT NULL,\n    \"jobid\" INT         NOT NULL,\n    \"name\"  VARCHAR(40) NOT NULL,\n    \"value\" TEXT        NULL,\n    PRIMARY KEY (\"id\"),\n    FOREIGN KEY (\"jobid\") REFERENCES \"job\" (\"id\") ON UPDATE CASCADE ON DELETE CASCADE\n);\n\nDO\n$$\n    BEGIN\n        BEGIN\n            CREATE INDEX \"ix_hangfire_jobparameter_jobidandname\" ON \"jobparameter\" (\"jobid\", \"name\");\n        EXCEPTION\n            WHEN duplicate_table THEN RAISE NOTICE 'INDEX \"ix_hangfire_jobparameter_jobidandname\" already exists.';\n        END;\n    END;\n$$;\n\nCREATE TABLE IF NOT EXISTS \"lock\"\n(\n    \"resource\" VARCHAR(100) NOT NULL,\n    UNIQUE (\"resource\")\n);\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v4.sql",
    "content": "﻿SET search_path = 'hangfire';\n--\n-- Table structure for table `Schema`\n--\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 4) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"counter\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"lock\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"hash\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"job\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"jobparameter\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"jobqueue\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"list\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"server\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"set\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\nALTER TABLE \"state\"\n    ADD COLUMN \"updatecount\" integer NOT NULL DEFAULT 0;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v5.sql",
    "content": "﻿SET search_path = 'hangfire';\n--\n-- Table structure for table `Schema`\n--\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 5) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"server\"\n    ALTER COLUMN \"id\" TYPE VARCHAR(100);\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v6.sql",
    "content": "SET search_path = 'hangfire';\n--\n-- Adds indices, greatly speeds-up deleting old jobs.\n--\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 6) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\n\nDO\n$$\n    BEGIN\n        BEGIN\n            CREATE INDEX \"ix_hangfire_counter_expireat\" ON \"counter\" (\"expireat\");\n        EXCEPTION\n            WHEN duplicate_table THEN RAISE NOTICE 'INDEX ix_hangfire_counter_expireat already exists.';\n        END;\n    END;\n$$;\n\nDO\n$$\n    BEGIN\n        BEGIN\n            CREATE INDEX \"ix_hangfire_jobqueue_jobidandqueue\" ON \"jobqueue\" (\"jobid\", \"queue\");\n        EXCEPTION\n            WHEN duplicate_table THEN RAISE NOTICE 'INDEX \"ix_hangfire_jobqueue_jobidandqueue\" already exists.';\n        END;\n    END;\n$$;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v7.sql",
    "content": "﻿SET search_path = 'hangfire';\n--\n-- Table structure for table `Schema`\n--\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 7) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"lock\"\n    ADD COLUMN acquired timestamp without time zone;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v8.sql",
    "content": "﻿SET search_path = 'hangfire';\n--\n-- Table structure for table `Schema`\n--\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 8) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"counter\"\n    ALTER COLUMN value TYPE bigint;\nALTER TABLE \"counter\"\n    DROP COLUMN updatecount RESTRICT;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Scripts/Install.v9.sql",
    "content": "SET search_path = 'hangfire';\n--\n-- Table structure for table `Schema`\n--\n\nDO\n$$\n    BEGIN\n        IF EXISTS(SELECT 1 FROM \"schema\" WHERE \"version\"::integer >= 9) THEN\n            RAISE EXCEPTION 'version-already-applied';\n        END IF;\n    END\n$$;\n\nALTER TABLE \"lock\"\n    ALTER COLUMN \"resource\" TYPE TEXT;\n\nRESET search_path;"
  },
  {
    "path": "src/Hangfire.PostgreSql/Utils/AutoResetEventRegistry.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Threading;\n\nnamespace Hangfire.PostgreSql.Utils\n{\n  /// <summary>\n  /// Represents a registry for managing AutoResetEvent instances using event keys.\n  /// </summary>\n  public class AutoResetEventRegistry\n  {\n    private readonly ConcurrentDictionary<string, AutoResetEvent> _events = new();\n\n    /// <summary>\n    /// Retrieves the wait handles associated with the specified event keys.\n    /// </summary>\n    /// <param name=\"eventKeys\">The event keys.</param>\n    /// <returns>An enumerable of wait handles.</returns>\n    public IEnumerable<WaitHandle> GetWaitHandles(IEnumerable<string> eventKeys)\n    {\n      foreach (string eventKey in eventKeys)\n      {\n          AutoResetEvent newHandle = _events.GetOrAdd(eventKey, _ => new AutoResetEvent(false));\n          yield return newHandle;\n      }\n    }\n\n    /// <summary>\n    /// Sets the specified event.\n    /// </summary>\n    /// <param name=\"eventKey\">The event key.</param>\n    public void Set(string eventKey)\n    {\n      if (_events.TryGetValue(eventKey, out AutoResetEvent handle))\n      {\n        handle.Set();\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/Hangfire.PostgreSql/Utils/DbConnectionExtensions.cs",
    "content": "﻿using System.Data;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Utils;\n\ninternal static class DbConnectionExtensions\n{\n  private static bool? _supportsNotifications;\n\n  internal static bool SupportsNotifications(this IDbConnection connection)\n  {\n    if (_supportsNotifications.HasValue)\n    {\n      return _supportsNotifications.Value;\n    }\n\n    if (connection is not NpgsqlConnection npgsqlConnection)\n    {\n      _supportsNotifications = false;\n      return false;\n    }\n\n    if (npgsqlConnection.State != ConnectionState.Open)\n    {\n      npgsqlConnection.Open();\n    }\n\n    _supportsNotifications = npgsqlConnection.PostgreSqlVersion.Major >= 11;\n    return _supportsNotifications.Value;\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Utils/ExceptionTypeHelper.cs",
    "content": "// This file is part of Hangfire. Copyright © 2022 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\n// Borrowed from Hangfire\n\nusing System;\n\nnamespace Hangfire.PostgreSql.Utils\n{\n  internal static class ExceptionTypeHelper\n  {\n#if !NETSTANDARD1_3\n    private static readonly Type StackOverflowType = typeof(StackOverflowException);\n#endif\n    private static readonly Type OutOfMemoryType = typeof(OutOfMemoryException);\n \n    internal static bool IsCatchableExceptionType(this Exception e)\n    {\n      var type = e.GetType();\n      return\n#if !NETSTANDARD1_3\n        type != StackOverflowType &&\n#endif\n        type != OutOfMemoryType;\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Utils/TimestampHelper.cs",
    "content": "// This file is part of Hangfire. Copyright © 2022 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\n// Borrowed from Hangfire\n\nusing System;\n\nnamespace Hangfire.PostgreSql.Utils\n{\n  internal static class TimestampHelper\n  {\n    public static long GetTimestamp()\n    {\n#if NETCOREAPP3_0\n            return Environment.TickCount64;\n#else\n      return Environment.TickCount;\n#endif\n    }\n\n    public static TimeSpan Elapsed(long timestamp)\n    {\n      long now = GetTimestamp();\n      return Elapsed(now, timestamp);\n    }\n\n    public static TimeSpan Elapsed(long now, long timestamp)\n    {\n#if NETCOREAPP3_0\n            return TimeSpan.FromMilliseconds(now - timestamp);\n#else\n      return TimeSpan.FromMilliseconds(unchecked((int)now - (int)timestamp));\n#endif\n    }\n  }\n}"
  },
  {
    "path": "src/Hangfire.PostgreSql/Utils/TransactionHelpers.cs",
    "content": "﻿using System;\nusing System.Transactions;\n\nnamespace Hangfire.PostgreSql.Utils\n{\n  public static class TransactionHelpers\n  {\n    internal static TransactionScope CreateTransactionScope(IsolationLevel? isolationLevel = IsolationLevel.ReadCommitted, bool enlist = true, TimeSpan? timeout = null)\n    {\n      TransactionScopeOption scopeOption = TransactionScopeOption.RequiresNew;\n      if (enlist)\n      {\n        Transaction currentTransaction = Transaction.Current;\n        if (currentTransaction != null)\n        {\n          isolationLevel = currentTransaction.IsolationLevel;\n          scopeOption = TransactionScopeOption.Required;\n        }\n      }\n\n      return new TransactionScope(\n        scopeOption,\n        new TransactionOptions {\n          IsolationLevel = isolationLevel.GetValueOrDefault(IsolationLevel.ReadCommitted),\n          Timeout = timeout.GetValueOrDefault(TransactionManager.DefaultTimeout),\n        });\n    }\n  }\n}\n"
  },
  {
    "path": "src/Hangfire.PostgreSql/Utils/TryExecute.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\n// ReSharper disable ArrangeDefaultValueWhenTypeNotEvident\n\nnamespace Hangfire.PostgreSql.Utils\n{\n  public static class Utils\n  {\n    public static bool TryExecute<T>(\n      Func<T> func,\n      out T result,\n      Func<Exception, bool> swallowException = default,\n      int? tryCount = default)\n    {\n      while (tryCount == default || tryCount-- > 0)\n      {\n        try\n        {\n          result = func();\n          return true;\n        }\n        catch (Exception ex)\n        {\n          if (swallowException != null && !swallowException(ex))\n          {\n            throw;\n          }\n        }\n      }\n      result = default;\n      return false;\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/AssemblyAttributes.cs",
    "content": "﻿using Xunit;\n\n// Running tests in parallel actually takes more time when we are cleaning the database for majority of tests. It's quicker to just let them run sequentially.\n[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)]"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/CountersAggregatorFacts.cs",
    "content": "﻿using System;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests;\n\npublic class CountersAggregatorFacts : IClassFixture<PostgreSqlStorageFixture>\n{\n  private static readonly string _schemaName = ConnectionUtils.GetSchemaName();\n\n  private readonly CancellationToken _token;\n  private readonly PostgreSqlStorageFixture _fixture;\n\n  public CountersAggregatorFacts(PostgreSqlStorageFixture fixture)\n  {\n    CancellationTokenSource cts = new();\n    _token = cts.Token;\n    _fixture = fixture;\n    _fixture.SetupOptions(o => o.CountersAggregateInterval = TimeSpan.FromMinutes(5));\n  }\n\n  [Fact]\n  [CleanDatabase]\n  public void Execute_AggregatesCounters()\n  {\n    UseConnection((connection, manager) => {\n      CreateEntry(1);\n      CreateEntry(5);\n      CreateEntry(15);\n      CreateEntry(5, \"key2\");\n      CreateEntry(10, \"key2\");\n\n      manager.Execute(_token);\n\n      Assert.Equal(21, GetAggregatedCounters(connection));\n      Assert.Equal(15, GetAggregatedCounters(connection, \"key2\"));\n      Assert.Null(GetRegularCounters(connection));\n      Assert.Null(GetRegularCounters(connection, \"key2\"));\n      return;\n\n      void CreateEntry(long value, string key = \"key\")\n      {\n        CreateCounterEntry(connection, value, key);\n      }\n    });\n  }\n\n  private void UseConnection(Action<NpgsqlConnection, CountersAggregator> action)\n  {\n    PostgreSqlStorage storage = _fixture.SafeInit();\n    CountersAggregator aggregator = new(storage, TimeSpan.Zero);\n    action(storage.CreateAndOpenConnection(), aggregator);\n  }\n\n  private static void CreateCounterEntry(NpgsqlConnection connection, long? value, string key = \"key\")\n  {\n    value ??= 1;\n    string insertSql =\n      $\"\"\"\n       INSERT INTO \"{_schemaName}\".\"counter\"(\"key\", \"value\", \"expireat\")\n       VALUES (@Key, @Value, null)\n       \"\"\";\n\n    connection.Execute(insertSql, new { Key = key, Value = value });\n  }\n\n  private static long GetAggregatedCounters(NpgsqlConnection connection, string key = \"key\")\n  {\n    return connection.QuerySingle<long>(\n      $\"\"\"\n       SELECT \"value\"\n       FROM {_schemaName}.\"aggregatedcounter\"\n       WHERE \"key\" = @Key \n       \"\"\", new { Key = key });\n  }\n\n  private static long? GetRegularCounters(NpgsqlConnection connection, string key = \"key\")\n  {\n    return connection.QuerySingle<long?>(\n      $\"\"\"\n         SELECT SUM(\"value\")\n         FROM {_schemaName}.\"counter\"\n         WHERE \"key\" = @Key\n         \"\"\", new { Key = key });\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Entities/TestJob.cs",
    "content": "using System;\n\nnamespace Hangfire.PostgreSql.Tests.Entities\n{\n  public record TestJob(long Id, string InvocationData, string Arguments, DateTime? ExpireAt, string StateName, long? StateId, DateTime CreatedAt);\n\n  public class TestJobs\n  {\n    public void Run(string logMessage)\n    {\n      Console.WriteLine(\"Running test job: {0}\", logMessage);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/ExpirationManagerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class ExpirationManagerFacts : IClassFixture<PostgreSqlStorageFixture>\n  {\n    private readonly PostgreSqlStorageFixture _fixture;\n    private readonly CancellationToken _token;\n\n    public ExpirationManagerFacts(PostgreSqlStorageFixture fixture)\n    {\n      CancellationTokenSource cts = new();\n      _token = cts.Token;\n      _fixture = fixture;\n      _fixture.SetupOptions(o => o.DeleteExpiredBatchSize = 2);\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenStorageIsNull()\n    {\n      Assert.Throws<ArgumentNullException>(() => new ExpirationManager(null));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_RemovesOutdatedRecords()\n    {\n      UseConnection((connection, manager) => {\n        long CreateEntry(string key)\n        {\n          return CreateExpirationEntry(connection, DateTime.UtcNow.AddMonths(-1), key);\n        }\n\n        List<long> entryIds = Enumerable.Range(1, 3).Select(i => CreateEntry($\"key{i}\")).ToList();\n\n        manager.Execute(_token);\n\n        entryIds.ForEach(entryId => Assert.True(IsEntryExpired(connection, entryId)));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_DoesNotRemoveEntries_WithNoExpirationTimeSet()\n    {\n      UseConnection((connection, manager) => {\n        long entryId = CreateExpirationEntry(connection, null);\n\n        manager.Execute(_token);\n\n        Assert.False(IsEntryExpired(connection, entryId));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_DoesNotRemoveEntries_WithFreshExpirationTime()\n    {\n      UseConnection((connection, manager) => {\n        long entryId = CreateExpirationEntry(connection, DateTime.Now.AddMonths(1));\n\n        manager.Execute(_token);\n\n        Assert.False(IsEntryExpired(connection, entryId));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_Processes_CounterTable()\n    {\n      UseConnection((connection, manager) => {\n        // Arrange\n        string createSql = $@\"\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"counter\"\" (\"\"key\"\", \"\"value\"\", \"\"expireat\"\") \n          VALUES ('key', 1, @ExpireAt)\n        \";\n        connection.Execute(createSql, new { ExpireAt = DateTime.UtcNow.AddMonths(-1) });\n\n        // Act\n        manager.Execute(_token);\n\n        // Assert\n        Assert.Equal(0, connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\"));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_Aggregates_CounterTable()\n    {\n      UseConnection((connection, manager) => {\n        // Arrange\n        string createSql = $@\"\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"counter\"\" (\"\"key\"\", \"\"value\"\") \n          VALUES ('stats:succeeded', 1)\n        \";\n        for (int i = 0; i < 5; i++)\n        {\n          connection.Execute(createSql);\n        }\n\n        // Act\n        manager.Execute(_token);\n\n        // Assert\n        Assert.Equal(1, connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\"));\n        Assert.Equal(5, connection.QuerySingle<long>($@\"SELECT SUM(\"\"value\"\") FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\"));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_Processes_JobTable()\n    {\n      UseConnection((connection, manager) => {\n        // Arrange\n        string createSql = $@\"\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\", \"\"expireat\"\") \n          VALUES ('{{}}', '[]', NOW(), @ExpireAt)\n        \";\n        connection.Execute(createSql, new { ExpireAt = DateTime.UtcNow.AddMonths(-1) });\n\n        // Act\n        manager.Execute(_token);\n\n        // Assert\n        Assert.Equal(0, connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"job\"\"\"));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_Processes_ListTable()\n    {\n      UseConnection((connection, manager) => {\n        // Arrange\n        string createSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"list\"\" (\"\"key\"\", \"\"expireat\"\") VALUES ('key', @ExpireAt)\";\n        connection.Execute(createSql, new { ExpireAt = DateTime.UtcNow.AddMonths(-1) });\n\n        // Act\n        manager.Execute(_token);\n\n        // Assert\n        Assert.Equal(0, connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\"));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_Processes_SetTable()\n    {\n      UseConnection((connection, manager) => {\n        // Arrange\n        string createSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"score\"\", \"\"value\"\", \"\"expireat\"\") VALUES ('key', 0, '', @ExpireAt)\";\n        connection.Execute(createSql, new { ExpireAt = DateTime.UtcNow.AddMonths(-1) });\n\n        // Act\n        manager.Execute(_token);\n\n        // Assert\n        Assert.Equal(0, connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\"));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Execute_Processes_HashTable()\n    {\n      UseConnection((connection, manager) => {\n        // Arrange\n        string createSql = $@\"\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"hash\"\" (\"\"key\"\", \"\"field\"\", \"\"value\"\", \"\"expireat\"\") \n          VALUES ('key', 'field', '', @ExpireAt)\";\n        connection.Execute(createSql, new { ExpireAt = DateTime.UtcNow.AddMonths(-1) });\n\n        // Act\n        manager.Execute(_token);\n\n        // Assert\n        Assert.Equal(0, connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"hash\"\"\"));\n      });\n    }\n\n    private static long CreateExpirationEntry(NpgsqlConnection connection, DateTime? expireAt, string key = \"key\")\n    {\n      string insertSqlNull = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"counter\"\"(\"\"key\"\", \"\"value\"\", \"\"expireat\"\")\n        VALUES (@Key, 1, null) RETURNING \"\"id\"\"\n      \";\n\n      string insertSqlValue = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"counter\"\"(\"\"key\"\", \"\"value\"\", \"\"expireat\"\")\n        VALUES (@Key, 1, NOW() - interval '{{0}} seconds') RETURNING \"\"id\"\"\n      \";\n\n      string insertSql = expireAt == null\n        ? insertSqlNull\n        : string.Format(CultureInfo.InvariantCulture, insertSqlValue,\n          ((long)(DateTime.UtcNow - expireAt.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture));\n\n      return connection.QuerySingle<long>(insertSql, new { Key = key });\n    }\n\n    private static bool IsEntryExpired(NpgsqlConnection connection, long entryId)\n    {\n      return connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\" WHERE \"\"id\"\" = @Id\", new { Id = entryId }) == 0;\n    }\n\n    private void UseConnection(Action<NpgsqlConnection, ExpirationManager> action)\n    {\n      PostgreSqlStorage storage = _fixture.SafeInit();\n      ExpirationManager manager = new ExpirationManager(storage, TimeSpan.Zero);\n      action(storage.CreateAndOpenConnection(), manager);\n    }\n\n    private static string GetSchemaName()\n    {\n      return ConnectionUtils.GetSchemaName();\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/FirstClassQueueFeatureSupportTests.cs",
    "content": "using System.Threading;\nusing Hangfire.PostgreSql.Tests.Entities;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Hangfire.Storage;\nusing Hangfire.Storage.Monitoring;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests;\n\npublic class FirstClassQueueFeatureSupportTests\n{\n  public FirstClassQueueFeatureSupportTests()\n  {\n    JobStorage.Current = new PostgreSqlStorage(ConnectionUtils.GetDefaultConnectionFactory());\n  }\n\n  [Fact]\n  public void HasFlag_ShouldReturnTrue_ForJobQueueProperty()\n  {\n    bool supportJobQueueProperty = JobStorage.Current.HasFeature(JobStorageFeatures.JobQueueProperty);\n    Assert.True(supportJobQueueProperty);\n  }\n\n  [Fact]\n  [CleanDatabase]\n  public void EnqueueJobWithSpecificQueue_ShouldEnqueueCorrectlyAndJobMustBeProcessedInThatQueue()\n  {\n    BackgroundJob.Enqueue<TestJobs>(\"critical\", job => job.Run(\"critical\"));\n    BackgroundJob.Enqueue<TestJobs>(\"offline\", job => job.Run(\"offline\"));\n\n    BackgroundJobServer unused = new(new BackgroundJobServerOptions() {\n      Queues = new[] { \"critical\" },\n    });\n\n    Thread.Sleep(200);\n\n    IMonitoringApi monitoringApi = JobStorage.Current.GetMonitoringApi();\n\n    JobList<EnqueuedJobDto> jobsInCriticalQueue = monitoringApi.EnqueuedJobs(\"critical\", 0, 10);\n    JobList<EnqueuedJobDto> jobsInOfflineQueue = monitoringApi.EnqueuedJobs(\"offline\", 0, 10);\n\n    Assert.Empty(jobsInCriticalQueue);   //Job from 'critical' queue must be processed by the server \n    Assert.NotEmpty(jobsInOfflineQueue); //Job from 'offline' queue must be left untouched because no server is processing it\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/GlobalSuppressions.cs",
    "content": "﻿// This file is used by Code Analysis to maintain SuppressMessage\n// attributes that are applied to this project.\n// Project-level suppressions either have no target or are given\n// a specific target and scoped to a namespace, type, member, etc.\n\nusing System.Diagnostics.CodeAnalysis;\n\n[assembly: SuppressMessage(\"Naming\", \"CA1707:Identifiers should not contain underscores\", Justification = \"This is a test project, underscores in test names are allowed\", Scope = \"module\")]\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Hangfire.PostgreSql.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <AssemblyTitle>Hangfire.PostgreSql.Tests</AssemblyTitle>\n    <TargetFramework>net9.0</TargetFramework>\n    <AssemblyName>Hangfire.PostgreSql.Tests</AssemblyName>\n    <PackageId>Hangfire.PostgreSql.Tests</PackageId>\n    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>\n    <LangVersion>default</LangVersion>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Include=\"Scripts\\Clean.sql\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.PostgreSql\\Hangfire.PostgreSql.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.0.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.3\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"xunit\" Version=\"2.4.1\" />\n    <PackageReference Include=\"Moq\" Version=\"4.16.1\" />\n    <PackageReference Include=\"Xunit.SkippableFact\" Version=\"1.4.13\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PersistentJobQueueProviderCollectionFacts.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PersistentJobQueueProviderCollectionFacts\n  {\n    private static readonly string[] _queues = { \"default\", \"critical\" };\n    private readonly Mock<IPersistentJobQueueProvider> _defaultProvider;\n    private readonly Mock<IPersistentJobQueueProvider> _provider;\n\n    public PersistentJobQueueProviderCollectionFacts()\n    {\n      _defaultProvider = new Mock<IPersistentJobQueueProvider>();\n      _provider = new Mock<IPersistentJobQueueProvider>();\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenDefaultProviderIsNull()\n    {\n      Assert.Throws<ArgumentNullException>(() => new PersistentJobQueueProviderCollection(null));\n    }\n\n    [Fact]\n    public void Enumeration_IncludesTheDefaultProvider()\n    {\n      PersistentJobQueueProviderCollection collection = CreateCollection();\n\n      IPersistentJobQueueProvider[] result = collection.ToArray();\n\n      Assert.Single(result);\n      Assert.Same(_defaultProvider.Object, result[0]);\n    }\n\n    [Fact]\n    public void GetProvider_ReturnsTheDefaultProvider_WhenProviderCanNotBeResolvedByQueue()\n    {\n      PersistentJobQueueProviderCollection collection = CreateCollection();\n\n      IPersistentJobQueueProvider provider = collection.GetProvider(\"queue\");\n\n      Assert.Same(_defaultProvider.Object, provider);\n    }\n\n    [Fact]\n    public void Add_ThrowsAnException_WhenProviderIsNull()\n    {\n      PersistentJobQueueProviderCollection collection = CreateCollection();\n\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => collection.Add(null, _queues));\n\n      Assert.Equal(\"provider\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Add_ThrowsAnException_WhenQueuesCollectionIsNull()\n    {\n      PersistentJobQueueProviderCollection collection = CreateCollection();\n\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => collection.Add(_provider.Object, null));\n\n      Assert.Equal(\"queues\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Enumeration_ContainsAddedProvider()\n    {\n      PersistentJobQueueProviderCollection collection = CreateCollection();\n\n      collection.Add(_provider.Object, _queues);\n\n      Assert.Contains(_provider.Object, collection);\n    }\n\n    [Fact]\n    public void GetProvider_CanBeResolved_ByAnyQueue()\n    {\n      PersistentJobQueueProviderCollection collection = CreateCollection();\n      collection.Add(_provider.Object, _queues);\n\n      IPersistentJobQueueProvider provider1 = collection.GetProvider(\"default\");\n      IPersistentJobQueueProvider provider2 = collection.GetProvider(\"critical\");\n\n      Assert.NotSame(_defaultProvider.Object, provider1);\n      Assert.Same(provider1, provider2);\n    }\n\n    private PersistentJobQueueProviderCollection CreateCollection()\n    {\n      return new PersistentJobQueueProviderCollection(_defaultProvider.Object);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlConnectionFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text.Json;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Transactions;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.PostgreSql.Factories;\nusing Hangfire.PostgreSql.Tests.Entities;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Moq;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlConnectionFacts : IClassFixture<PostgreSqlStorageFixture>\n  {\n    private readonly PostgreSqlStorageFixture _fixture;\n\n    public PostgreSqlConnectionFacts(PostgreSqlStorageFixture fixture)\n    {\n      _fixture = fixture;\n      _fixture.SetupOptions(o => o.TransactionSynchronisationTimeout = TimeSpan.FromSeconds(2));\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenStorageIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlConnection(null));\n\n      Assert.Equal(\"storage\", exception.ParamName);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Ctor_ThrowsAnException_WhenOptionsIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(\n        () => new PostgreSqlConnection(new PostgreSqlStorage(ConnectionUtils.GetDefaultConnectionFactory(), null)));\n\n      Assert.Equal(\"options\", exception.ParamName);\n    }\n\n\n    [Fact]\n    [CleanDatabase]\n    public void FetchNextJob_DelegatesItsExecution_ToTheQueue()\n    {\n      UseConnection(connection => {\n        CancellationToken token = new();\n        string[] queues = { \"default\" };\n\n        connection.FetchNextJob(queues, token);\n\n        _fixture.PersistentJobQueueMock.Verify(x => x.Dequeue(queues, token));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void FetchNextJob_Throws_IfMultipleProvidersResolved()\n    {\n      UseConnection(connection => {\n        CancellationToken token = new CancellationToken();\n        Mock<IPersistentJobQueueProvider> anotherProvider = new Mock<IPersistentJobQueueProvider>();\n        _fixture.PersistentJobQueueProviderCollection.Add(anotherProvider.Object, new[] { \"critical\" });\n\n        try\n        {\n          Assert.Throws<InvalidOperationException>(() => connection.FetchNextJob(new[] { \"critical\", \"default\" }, token));\n        }\n        finally\n        {\n          _fixture.PersistentJobQueueProviderCollection.Remove(\"critical\");\n        }\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void CreateWriteTransaction_ReturnsNonNullInstance()\n    {\n      UseConnection(connection => {\n        IWriteOnlyTransaction transaction = connection.CreateWriteTransaction();\n        Assert.NotNull(transaction);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AcquireLock_ReturnsNonNullInstance()\n    {\n      UseConnection(connection => {\n        IDisposable distributedLock = connection.AcquireDistributedLock(\"1\", TimeSpan.FromSeconds(1));\n        Assert.NotNull(distributedLock);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void CreateExpiredJob_ThrowsAnException_WhenJobIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.CreateExpiredJob(null,\n          new Dictionary<string, string>(),\n          DateTime.UtcNow,\n          TimeSpan.Zero));\n\n        Assert.Equal(\"job\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void CreateExpiredJob_ThrowsAnException_WhenParametersCollectionIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.CreateExpiredJob(\n          Job.FromExpression(() => SampleMethod(\"hello\")),\n          null,\n          DateTime.UtcNow,\n          TimeSpan.Zero));\n\n        Assert.Equal(\"parameters\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void CreateExpiredJob_CreatesAJobInTheStorage_AndSetsItsParameters()\n    {\n      UseConnections((connection, jobStorageConnection) => {\n        DateTime createdAt = new DateTime(2012, 12, 12, 0, 0, 0, DateTimeKind.Utc);\n        string jobId = jobStorageConnection.CreateExpiredJob(Job.FromExpression(() => SampleMethod(\"Hello\")),\n          new Dictionary<string, string> { { \"Key1\", \"Value1\" }, { \"Key2\", \"Value2\" } },\n          createdAt,\n          TimeSpan.FromDays(1));\n\n        Assert.NotNull(jobId);\n        Assert.NotEmpty(jobId);\n\n        TestJob testJob = Helper.GetTestJob(connection, GetSchemaName(), \"-1\");\n        Assert.Equal(jobId, testJob.Id.ToString(CultureInfo.InvariantCulture));\n        Assert.Equal(createdAt, testJob.CreatedAt);\n        Assert.Null(testJob.StateId);\n        Assert.Null(testJob.StateName);\n\n        InvocationData invocationData = SerializationHelper.Deserialize<InvocationData>(testJob.InvocationData);\n        invocationData.Arguments = testJob.Arguments;\n\n        Job job = invocationData.DeserializeJob();\n        Assert.Equal(typeof(PostgreSqlConnectionFacts), job.Type);\n        Assert.Equal(\"SampleMethod\", job.Method.Name);\n        Assert.Equal(\"Hello\", job.Args[0]);\n\n        Assert.True(createdAt.AddDays(1).AddMinutes(-1) < testJob.ExpireAt);\n        Assert.True(testJob.ExpireAt < createdAt.AddDays(1).AddMinutes(1));\n\n        Dictionary<string, string> parameters = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobparameter\"\" WHERE \"\"jobid\"\" = @Id\",\n            new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture) })\n          .ToDictionary(x => (string)x.name, x => (string)x.value);\n\n        Assert.Equal(\"Value1\", parameters[\"Key1\"]);\n        Assert.Equal(\"Value2\", parameters[\"Key2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetJobData_ThrowsAnException_WhenJobIdIsNull()\n    {\n      UseConnection(connection => Assert.Throws<ArgumentNullException>(() => connection.GetJobData(null)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetJobData_ReturnsNull_WhenThereIsNoSuchJob()\n    {\n      UseConnection(connection => {\n        JobData result = connection.GetJobData(\"1\");\n        Assert.Null(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetJobData_ReturnsResult_WhenJobExists()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"statename\"\", \"\"createdat\"\")\n        VALUES (@InvocationData::jsonb, @Arguments::jsonb, @StateName, NOW()) RETURNING \"\"id\"\"\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        Job job = Job.FromExpression(() => SampleMethod(\"wrong\"));\n\n        long jobId = connection.QuerySingle<long>(arrangeSql,\n          new {\n            InvocationData = JsonParameter.GetParameterValue(SerializationHelper.Serialize(InvocationData.SerializeJob(job))),\n            StateName = \"Succeeded\",\n            Arguments = JsonParameter.GetParameterValue(\"[\\\"\\\\\\\"Arguments\\\\\\\"\\\"]\", JsonParameter.ValueType.Array),\n          });\n\n        JobData result = jobStorageConnection.GetJobData(jobId.ToString(CultureInfo.InvariantCulture));\n\n        Assert.NotNull(result);\n        Assert.NotNull(result.Job);\n        Assert.Equal(\"Succeeded\", result.State);\n        Assert.Equal(\"Arguments\", result.Job.Args[0]);\n        Assert.Null(result.LoadException);\n        Assert.True(DateTime.UtcNow.AddMinutes(-1) < result.CreatedAt);\n        Assert.True(result.CreatedAt < DateTime.UtcNow.AddMinutes(1));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetStateData_ThrowsAnException_WhenJobIdIsNull()\n    {\n      UseConnection(connection => Assert.Throws<ArgumentNullException>(() => connection.GetStateData(null)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetStateData_ReturnsNull_IfThereIsNoSuchState()\n    {\n      UseConnection(connection => {\n        StateData result = connection.GetStateData(\"1\");\n        Assert.Null(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetStateData_ReturnsCorrectData()\n    {\n      string createJobSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"statename\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', '', NOW()) RETURNING \"\"id\"\";\n      \";\n\n      string createStateSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"state\"\" (\"\"jobid\"\", \"\"name\"\", \"\"createdat\"\")\n        VALUES(@JobId, 'old-state', NOW());\n\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"state\"\" (\"\"jobid\"\", \"\"name\"\", \"\"reason\"\", \"\"data\"\", \"\"createdat\"\")\n        VALUES(@JobId, @Name, @Reason, @Data::jsonb, NOW())\n        RETURNING \"\"id\"\";\n      \";\n\n      string updateJobStateSql = $@\"\n        UPDATE \"\"{GetSchemaName()}\"\".\"\"job\"\"\n        SET \"\"stateid\"\" = @StateId\n        WHERE \"\"id\"\" = @JobId;\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        Dictionary<string, string> data = new() {\n          { \"Key\", \"Value\" },\n        };\n\n        long jobId = connection.QuerySingle<long>(createJobSql);\n\n        long stateId = connection.QuerySingle<long>(createStateSql,\n          new { JobId = jobId, Name = \"Name\", Reason = \"Reason\", Data = JsonParameter.GetParameterValue(SerializationHelper.Serialize(data)) });\n\n        connection.Execute(updateJobStateSql, new { JobId = jobId, StateId = stateId });\n\n        StateData result = jobStorageConnection.GetStateData(jobId.ToString(CultureInfo.InvariantCulture));\n        Assert.NotNull(result);\n\n        Assert.Equal(\"Name\", result.Name);\n        Assert.Equal(\"Reason\", result.Reason);\n        Assert.Equal(\"Value\", result.Data[\"Key\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetParameter_ThrowsAnException_WhenJobIdIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.SetJobParameter(null, \"name\", \"value\"));\n\n        Assert.Equal(\"id\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetParameter_ThrowsAnException_WhenNameIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.SetJobParameter(\"1\", null, \"value\"));\n\n        Assert.Equal(\"name\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetParameters_CreatesNewParameter_WhenParameterWithTheGivenNameDoesNotExists()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', NOW()) RETURNING \"\"id\"\"\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        string jobId = connection.QuerySingle<long>(arrangeSql).ToString(CultureInfo.InvariantCulture);\n\n        jobStorageConnection.SetJobParameter(jobId, \"Name\", \"Value\");\n\n        string parameterValue = connection.QuerySingle<string>($@\"SELECT \"\"value\"\" FROM \"\"{GetSchemaName()}\"\".\"\"jobparameter\"\" WHERE \"\"jobid\"\" = @Id AND \"\"name\"\" = @Name\",\n          new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture), Name = \"Name\" });\n\n        Assert.Equal(\"Value\", parameterValue);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetParameter_UpdatesValue_WhenParameterWithTheGivenName_AlreadyExists()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', NOW()) RETURNING \"\"id\"\"\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        string jobId = connection.QuerySingle<long>(arrangeSql).ToString(CultureInfo.InvariantCulture);\n\n        jobStorageConnection.SetJobParameter(jobId, \"Name\", \"Value\");\n        jobStorageConnection.SetJobParameter(jobId, \"Name\", \"AnotherValue\");\n\n        string parameterValue = connection.QuerySingle<string>($@\"SELECT \"\"value\"\" FROM \"\"{GetSchemaName()}\"\".\"\"jobparameter\"\" WHERE \"\"jobid\"\" = @Id AND \"\"name\"\" = @Name\",\n          new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture), Name = \"Name\" });\n\n        Assert.Equal(\"AnotherValue\", parameterValue);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetParameter_CanAcceptNulls_AsValues()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', NOW()) RETURNING \"\"id\"\"\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        string jobId = connection.QuerySingle<long>(arrangeSql).ToString(CultureInfo.InvariantCulture);\n\n        jobStorageConnection.SetJobParameter(jobId, \"Name\", null);\n\n        string parameterValue = connection.QuerySingle<string>($@\"SELECT \"\"value\"\" FROM \"\"{GetSchemaName()}\"\".\"\"jobparameter\"\" WHERE \"\"jobid\"\" = @Id AND \"\"name\"\" = @Name\",\n          new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture), Name = \"Name\" });\n\n        Assert.Null(parameterValue);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetParameter_ThrowsAnException_WhenJobIdIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.GetJobParameter(null, \"hello\"));\n\n        Assert.Equal(\"id\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetParameter_ThrowsAnException_WhenNameIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.GetJobParameter(\"1\", null));\n\n        Assert.Equal(\"name\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetParameter_ReturnsNull_WhenParameterDoesNotExists()\n    {\n      UseConnection(connection => {\n        string value = connection.GetJobParameter(\"1\", \"hello\");\n        Assert.Null(value);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetParameter_ReturnsParameterValue_WhenJobExists()\n    {\n      string arrangeSql = $@\"\n        WITH \"\"insertedjob\"\" AS (\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n          VALUES ('{{}}', '[]', NOW()) RETURNING \"\"id\"\"\n        )\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobparameter\"\" (\"\"jobid\"\", \"\"name\"\", \"\"value\"\")\n        SELECT \"\"insertedjob\"\".\"\"id\"\", @Name, @Value\n        FROM \"\"insertedjob\"\"\n        RETURNING \"\"jobid\"\";\n      \";\n      UseConnections((connection, jobStorageConnection) => {\n        long id = connection.QuerySingle<long>(arrangeSql,\n          new { Name = \"name\", Value = \"value\" });\n\n        string value = jobStorageConnection.GetJobParameter(Convert.ToString(id, CultureInfo.InvariantCulture), \"name\");\n\n        Assert.Equal(\"value\", value);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.GetFirstByLowestScoreFromSet(null, 0, 1));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_ThrowsAnException_ToScoreIsLowerThanFromScore()\n    {\n      UseConnection(connection => Assert.Throws<ArgumentException>(() => connection.GetFirstByLowestScoreFromSet(\"key\", 0, -1)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_ReturnsNull_WhenTheKeyDoesNotExist()\n    {\n      UseConnection(connection => {\n        string result = connection.GetFirstByLowestScoreFromSet(\"key\", 0, 1);\n\n        Assert.Null(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_ReturnsTheValueWithTheLowestScore()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"score\"\", \"\"value\"\")\n        VALUES \n        ('key', 1.0, '1.0'),\n        ('key', -1.0, '-1.0'),\n        ('key', -5.0, '-5.0'),\n        ('another-key', -2.0, '-2.0')\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql);\n\n        string result = jobStorageConnection.GetFirstByLowestScoreFromSet(\"key\", -1.0, 3.0);\n\n        Assert.Equal(\"-1.0\", result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_List_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.GetFirstByLowestScoreFromSet(null, 0, 1, 1));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_List_ThrowsAnException_WhenToScoreIsLowerThanFromScore()\n    {\n      UseConnection(connection => {\n        ArgumentException exception = Assert.Throws<ArgumentException>(() => connection.GetFirstByLowestScoreFromSet(\"key\", 0, -1, 1));\n\n        Assert.Contains(\"The 'toScore' value must be higher or equal to the 'fromScore' value.\", exception.Message);\n      });\n    }\n\n    [Theory]\n    [CleanDatabase]\n    [InlineData(-1)]\n    [InlineData(0)]\n    public void GetFirstByLowestScoreFromSet_List_ThrowsAnException_WhenCountIsLessThanOne(int count)\n    {\n      UseConnection(connection => {\n        ArgumentException exception = Assert.Throws<ArgumentException>(() => connection.GetFirstByLowestScoreFromSet(\"key\", 0, 1, count));\n\n        Assert.Contains(\"The 'count' value must be greater than zero (0).\", exception.Message);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_List_ReturnsEmpty_WhenTheKeyDoesNotExist()\n    {\n      UseConnection(connection => {\n        List<string> result = connection.GetFirstByLowestScoreFromSet(\"key\", 0, 1, 1);\n\n        Assert.NotNull(result);\n        Assert.Empty(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_List_ReturnsEmpty_WhenNoValuesExistForKey()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"score\"\", \"\"value\"\")\n        VALUES \n        ('another-key', -2.0, '-2.0')\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql);\n\n        List<string> result = jobStorageConnection.GetFirstByLowestScoreFromSet(\"key\", 0, 1, 1);\n\n        Assert.NotNull(result);\n        Assert.Empty(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_List_ReturnsAllLowestValuesMatchingInputs()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"score\"\", \"\"value\"\")\n        VALUES \n        ('key', 1.0, '1.0'),\n        ('key', -1.0, '-1.0'),\n        ('key', -5.0, '-5.0'),\n        ('another-key', -2.0, '-2.0')\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql);\n\n        List<string> result = jobStorageConnection.GetFirstByLowestScoreFromSet(\"key\", -1.0, 3.0, 10);\n\n        Assert.Equal(2, result.Count);\n        Assert.Equal(\"-1.0\", result[0]);\n        Assert.Equal(\"1.0\", result[1]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetFirstByLowestScoreFromSet_List_ReturnsSubsetOfLowestValuesMatchingInputs()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"score\"\", \"\"value\"\")\n        VALUES \n        ('key', 1.0, '1.0'),\n        ('key', 1.5, '1.5'),\n        ('key', 2.0, '2.0'),\n        ('key', 2.5, '2.5'),\n        ('key', -1.0, '-1.0'),\n        ('key', -5.0, '-5.0'),\n        ('another-key', -2.0, '-2.0')\n      \";\n\n      int count = 3;\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql);\n\n        List<string> result = jobStorageConnection.GetFirstByLowestScoreFromSet(\"key\", -1.0, 3.0, count);\n\n        Assert.Equal(count, result.Count);\n        Assert.Equal(\"-1.0\", result[0]);\n        Assert.Equal(\"1.0\", result[1]);\n        Assert.Equal(\"1.5\", result[2]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AnnounceServer_ThrowsAnException_WhenServerIdIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.AnnounceServer(null, new ServerContext()));\n\n        Assert.Equal(\"serverId\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AnnounceServer_ThrowsAnException_WhenContextIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.AnnounceServer(\"server\", null));\n\n        Assert.Equal(\"context\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AnnounceServer_CreatesOrUpdatesARecord()\n    {\n      UseConnections((connection, jobStorageConnection) => {\n        ServerContext context1 = new ServerContext {\n          WorkerCount = 4,\n          Queues = new[] { \"critical\", \"default\" },\n        };\n        jobStorageConnection.AnnounceServer(\"server\", context1);\n\n        dynamic server = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"server\"\"\");\n        Assert.Equal(\"server\", server.id);\n\n        ServerContext serverData = JsonSerializer.Deserialize<ServerContext>(server.data);\n        Assert.Equal(4, serverData.WorkerCount);\n        Assert.Equal(context1.Queues, serverData.Queues);\n        Assert.NotNull(server.lastheartbeat);\n\n        ServerContext context2 = new ServerContext {\n          Queues = new[] { \"default\" },\n          WorkerCount = 1000,\n        };\n        jobStorageConnection.AnnounceServer(\"server\", context2);\n        dynamic sameServer = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"server\"\"\");\n        Assert.Equal(\"server\", sameServer.id);\n        Assert.Contains(\"1000\", sameServer.data);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveServer_ThrowsAnException_WhenServerIdIsNull()\n    {\n      UseConnection(connection => Assert.Throws<ArgumentNullException>(() => connection.RemoveServer(null)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveServer_RemovesAServerRecord()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"server\"\" (\"\"id\"\", \"\"data\"\", \"\"lastheartbeat\"\")\n        VALUES ('Server1', '{{}}', NOW()),\n        ('Server2', '{{}}', NOW())\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql);\n\n        jobStorageConnection.RemoveServer(\"Server1\");\n\n        dynamic server = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"server\"\"\");\n        Assert.NotEqual(\"Server1\", server.Id, StringComparer.OrdinalIgnoreCase);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Heartbeat_ThrowsAnException_WhenServerIdIsNull()\n    {\n      UseConnection(connection => Assert.Throws<ArgumentNullException>(() => connection.Heartbeat(null)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Heartbeat_ThrowsBackgroundServerGoneException_WhenServerDisappeared()\n    {\n      string disappearedServerId = Guid.NewGuid().ToString();\n\n      UseConnection(connection => Assert.Throws<BackgroundServerGoneException>(() => connection.Heartbeat(disappearedServerId)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Heartbeat_UpdatesLastHeartbeat_OfTheServerWithGivenId()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"server\"\" (\"\"id\"\", \"\"data\"\", \"\"lastheartbeat\"\")\n        VALUES ('server1', '{{}}', '2012-12-12 12:12:12'), ('server2', '{{}}', '2012-12-12 12:12:12')\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql);\n\n        jobStorageConnection.Heartbeat(\"server1\");\n\n        Dictionary<string, DateTime> servers = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"server\"\"\")\n          .ToDictionary(x => (string)x.id, x => (DateTime)x.lastheartbeat);\n\n        Assert.NotEqual(2012, servers[\"server1\"].Year);\n        Assert.Equal(2012, servers[\"server2\"].Year);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveTimedOutServers_ThrowsAnException_WhenTimeOutIsNegative()\n    {\n      UseConnection(connection => Assert.Throws<ArgumentException>(() => connection.RemoveTimedOutServers(TimeSpan.FromMinutes(-5))));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveTimedOutServers_DoItsWorkPerfectly()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"server\"\" (\"\"id\"\", \"\"data\"\", \"\"lastheartbeat\"\")\n        VALUES (@Id, '{{}}', @Heartbeat)\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql,\n          new[] {\n            new { Id = \"server1\", Heartbeat = DateTime.UtcNow.AddDays(-1) },\n            new { Id = \"server2\", Heartbeat = DateTime.UtcNow.AddHours(-12) },\n          });\n\n        jobStorageConnection.RemoveTimedOutServers(TimeSpan.FromHours(15));\n\n        dynamic liveServer = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"server\"\"\");\n        Assert.Equal(\"server2\", liveServer.id);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllItemsFromSet_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection =>\n        Assert.Throws<ArgumentNullException>(() => connection.GetAllItemsFromSet(null)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllItemsFromSet_ReturnsEmptyCollection_WhenKeyDoesNotExist()\n    {\n      UseConnection(connection => {\n        HashSet<string> result = connection.GetAllItemsFromSet(\"some-set\");\n\n        Assert.NotNull(result);\n        Assert.Empty(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllItemsFromSet_ReturnsAllItems()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"score\"\", \"\"value\"\")\n        VALUES (@Key, 0.0, @Value)\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"some-set\", Value = \"1\" },\n          new { Key = \"some-set\", Value = \"2\" },\n          new { Key = \"another-set\", Value = \"3\" },\n        });\n\n        // Act\n        HashSet<string> result = jobStorageConnection.GetAllItemsFromSet(\"some-set\");\n\n        // Assert\n        Assert.Equal(2, result.Count);\n        Assert.Contains(\"1\", result);\n        Assert.Contains(\"2\", result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetRangeInHash_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.SetRangeInHash(null, new Dictionary<string, string>()));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetRangeInHash_ThrowsAnException_WhenKeyValuePairsArgumentIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.SetRangeInHash(\"some-hash\", null));\n\n        Assert.Equal(\"keyValuePairs\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetRangeInHash_MergesAllRecords()\n    {\n      UseConnections((connection, jobStorageConnection) => {\n        jobStorageConnection.SetRangeInHash(\"some-hash\", new Dictionary<string, string> {\n          { \"Key1\", \"Value1\" },\n          { \"Key2\", \"Value2\" },\n        });\n\n        Dictionary<string, string> result = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"hash\"\" WHERE \"\"key\"\" = @Key\",\n            new { Key = \"some-hash\" })\n          .ToDictionary(x => (string)x.field, x => (string)x.value);\n\n        Assert.Equal(\"Value1\", result[\"Key1\"]);\n        Assert.Equal(\"Value2\", result[\"Key2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetRangeInHash_DoesNotThrowSerializationException()\n    {\n      Parallel.For(1, 100, _ => {\n        UseDisposableConnection(connection => {\n          connection.SetRangeInHash(\"some-hash\", new Dictionary<string, string> {\n            { \"Key1\", \"Value1\" },\n            { \"Key2\", \"Value2\" },\n          });\n        });\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllEntriesFromHash_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection =>\n        Assert.Throws<ArgumentNullException>(() => connection.GetAllEntriesFromHash(null)));\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllEntriesFromHash_ReturnsNull_IfHashDoesNotExist()\n    {\n      UseConnection(connection => {\n        Dictionary<string, string> result = connection.GetAllEntriesFromHash(\"some-hash\");\n        Assert.Null(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllEntriesFromHash_ReturnsAllKeysAndTheirValues()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"hash\"\" (\"\"key\"\", \"\"field\"\", \"\"value\"\")\n        VALUES (@Key, @Field, @Value)\n      \";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"some-hash\", Field = \"Key1\", Value = \"Value1\" },\n          new { Key = \"some-hash\", Field = \"Key2\", Value = \"Value2\" },\n          new { Key = \"another-hash\", Field = \"Key3\", Value = \"Value3\" },\n        });\n\n        // Act\n        Dictionary<string, string> result = jobStorageConnection.GetAllEntriesFromHash(\"some-hash\");\n\n        // Assert\n        Assert.NotNull(result);\n        Assert.Equal(2, result.Count);\n        Assert.Equal(\"Value1\", result[\"Key1\"]);\n        Assert.Equal(\"Value2\", result[\"Key2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetSetCount_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetSetCount(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetSetCount_ReturnsZero_WhenSetDoesNotExist()\n    {\n      UseConnection(connection => {\n        long result = connection.GetSetCount(\"my-set\");\n        Assert.Equal(0, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetSetCount_ReturnsNumberOfElements_InASet()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".set (key, value, score) VALUES (@Key, @Value, 0.0)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"set-1\", Value = \"value-1\" },\n          new { Key = \"set-2\", Value = \"value-1\" },\n          new { Key = \"set-1\", Value = \"value-2\" },\n        });\n\n        long result = jobStorageConnection.GetSetCount(\"set-1\");\n\n        Assert.Equal(2, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllItemsFromList_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetAllItemsFromList(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllItemsFromList_ReturnsAnEmptyList_WhenListDoesNotExist()\n    {\n      UseConnection(connection => {\n        List<string> result = connection.GetAllItemsFromList(\"my-list\");\n        Assert.Empty(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetAllItemsFromList_ReturnsAllItems_FromAGivenList()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".list (key, value) VALUES (@Key, @Value)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"list-1\", Value = \"1\" },\n          new { Key = \"list-2\", Value = \"2\" },\n          new { Key = \"list-1\", Value = \"3\" },\n        });\n\n        // Act\n        List<string> result = jobStorageConnection.GetAllItemsFromList(\"list-1\");\n\n        // Assert\n        Assert.Equal(new[] { \"3\", \"1\" }, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetCounter_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetCounter(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetCounter_ReturnsZero_WhenKeyDoesNotExist()\n    {\n      UseConnection(connection => {\n        long result = connection.GetCounter(\"my-counter\");\n        Assert.Equal(0, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetCounter_ReturnsSumOfValues_InCounterTable()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".counter (key, value) VALUES (@Key, @Value)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"counter-1\", Value = 1 },\n          new { Key = \"counter-2\", Value = 1 },\n          new { Key = \"counter-1\", Value = 1 },\n        });\n\n        // Act\n        long result = jobStorageConnection.GetCounter(\"counter-1\");\n\n        // Assert\n        Assert.Equal(2, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetListCount_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetListCount(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetListCount_ReturnsZero_WhenListDoesNotExist()\n    {\n      UseConnection(connection => {\n        long result = connection.GetListCount(\"my-list\");\n        Assert.Equal(0, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetListCount_ReturnsTheNumberOfListElements()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"list\"\"(\"\"key\"\") VALUES (@Key)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"list-1\" },\n          new { Key = \"list-1\" },\n          new { Key = \"list-2\" },\n        });\n\n        // Act\n        long result = jobStorageConnection.GetListCount(\"list-1\");\n\n        // Assert\n        Assert.Equal(2, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetListTtl_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetListTtl(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetListTtl_ReturnsNegativeValue_WhenListDoesNotExist()\n    {\n      UseConnection(connection => {\n        TimeSpan result = connection.GetListTtl(\"my-list\");\n        Assert.True(result < TimeSpan.Zero);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetListTtl_ReturnsExpirationTimeForList()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".list (key, expireat) VALUES (@Key, @ExpireAt)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"list-1\", ExpireAt = (DateTime?)DateTime.UtcNow.AddHours(1) },\n          new { Key = \"list-2\", ExpireAt = (DateTime?)null },\n        });\n\n        // Act\n        TimeSpan result = jobStorageConnection.GetListTtl(\"list-1\");\n\n        // Assert\n        Assert.True(TimeSpan.FromMinutes(59) < result);\n        Assert.True(result < TimeSpan.FromMinutes(61));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetRangeFromList_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.GetRangeFromList(null, 0, 1));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetRangeFromList_ReturnsAnEmptyList_WhenListDoesNotExist()\n    {\n      UseConnection(connection => {\n        List<string> result = connection.GetRangeFromList(\"my-list\", 0, 1);\n        Assert.Empty(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetRangeFromList_ReturnsAllEntries_WithinGivenBounds()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".list (key, value) VALUES (@Key, @Value)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"list-1\", Value = \"1\" },\n          new { Key = \"list-2\", Value = \"2\" },\n          new { Key = \"list-1\", Value = \"3\" },\n          new { Key = \"list-1\", Value = \"4\" },\n          new { Key = \"list-1\", Value = \"5\" },\n        });\n\n        // Act\n        List<string> result = jobStorageConnection.GetRangeFromList(\"list-1\", 1, 2);\n\n        // Assert\n        Assert.Equal(new[] { \"4\", \"3\" }, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetHashCount_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetHashCount(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetHashCount_ReturnsZero_WhenKeyDoesNotExist()\n    {\n      UseConnection(connection => {\n        long result = connection.GetHashCount(\"my-hash\");\n        Assert.Equal(0, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetHashCount_ReturnsNumber_OfHashFields()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".hash (key, field) VALUES (@Key, @Field)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"hash-1\", Field = \"field-1\" },\n          new { Key = \"hash-1\", Field = \"field-2\" },\n          new { Key = \"hash-2\", Field = \"field-1\" },\n        });\n\n        // Act\n        long result = jobStorageConnection.GetHashCount(\"hash-1\");\n\n        // Assert\n        Assert.Equal(2, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetHashTtl_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetHashTtl(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetHashTtl_ReturnsNegativeValue_WhenHashDoesNotExist()\n    {\n      UseConnection(connection => {\n        TimeSpan result = connection.GetHashTtl(\"my-hash\");\n        Assert.True(result < TimeSpan.Zero);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetHashTtl_ReturnsExpirationTimeForHash()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".hash (key, field, expireat) VALUES (@Key, @Field, @ExpireAt)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"hash-1\", Field = \"field\", ExpireAt = (DateTime?)DateTime.UtcNow.AddHours(1) },\n          new { Key = \"hash-2\", Field = \"field\", ExpireAt = (DateTime?)null },\n        });\n\n        // Act\n        TimeSpan result = jobStorageConnection.GetHashTtl(\"hash-1\");\n\n        // Assert\n        Assert.True(TimeSpan.FromMinutes(59) < result);\n        Assert.True(result < TimeSpan.FromMinutes(61));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetRangeFromSet_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetRangeFromSet(null, 0, 1)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetRangeFromSet_ReturnsPagedElements()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".set (key, value, score) VALUES (@Key, @Value, 0.0)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"set-1\", Value = \"1\" },\n          new { Key = \"set-1\", Value = \"2\" },\n          new { Key = \"set-1\", Value = \"3\" },\n          new { Key = \"set-1\", Value = \"4\" },\n          new { Key = \"set-2\", Value = \"4\" },\n          new { Key = \"set-1\", Value = \"5\" },\n        });\n\n        List<string> result = jobStorageConnection.GetRangeFromSet(\"set-1\", 2, 3);\n\n        Assert.Equal(new[] { \"3\", \"4\" }, result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetSetTtl_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => connection.GetSetTtl(null)); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetSetTtl_ReturnsNegativeValue_WhenSetDoesNotExist()\n    {\n      UseConnection(connection => {\n        TimeSpan result = connection.GetSetTtl(\"my-set\");\n        Assert.True(result < TimeSpan.Zero);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetSetTtl_ReturnsExpirationTime_OfAGivenSet()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".set (key, value, expireat, score) VALUES (@Key, @Value, @ExpireAt, 0.0)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"set-1\", Value = \"1\", ExpireAt = (DateTime?)DateTime.UtcNow.AddMinutes(60) },\n          new { Key = \"set-2\", Value = \"2\", ExpireAt = (DateTime?)null },\n        });\n\n        // Act\n        TimeSpan result = jobStorageConnection.GetSetTtl(\"set-1\");\n\n        // Assert\n        Assert.True(TimeSpan.FromMinutes(59) < result);\n        Assert.True(result < TimeSpan.FromMinutes(61));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetValueFromHash_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.GetValueFromHash(null, \"name\"));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetValueFromHash_ThrowsAnException_WhenNameIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => connection.GetValueFromHash(\"key\", null));\n\n        Assert.Equal(\"name\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetValueFromHash_ReturnsNull_WhenHashDoesNotExist()\n    {\n      UseConnection(connection => {\n        string result = connection.GetValueFromHash(\"my-hash\", \"name\");\n        Assert.Null(result);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetValueFromHash_ReturnsValue_OfAGivenField()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".hash (key, field, value) VALUES (@Key, @Field, @Value)\";\n\n      UseConnections((connection, jobStorageConnection) => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"hash-1\", Field = \"field-1\", Value = \"1\" },\n          new { Key = \"hash-1\", Field = \"field-2\", Value = \"2\" },\n          new { Key = \"hash-2\", Field = \"field-1\", Value = \"3\" },\n        });\n\n        // Act\n        string result = jobStorageConnection.GetValueFromHash(\"hash-1\", \"field-1\");\n\n        // Assert\n        Assert.Equal(\"1\", result);\n      });\n    }\n\n    [Theory]\n    [CleanDatabase]\n    [InlineData(false)]\n    [InlineData(true)]\n    public void CreateExpiredJob_EnlistsInTransaction(bool completeTransactionScope)\n    {\n      TransactionScope CreateTransactionScope()\n      {\n        TransactionOptions transactionOptions = new TransactionOptions() {\n          IsolationLevel = IsolationLevel.ReadCommitted,\n          Timeout = TransactionManager.MaximumTimeout,\n        };\n\n        return new TransactionScope(TransactionScopeOption.Required, transactionOptions);\n      }\n\n      string jobId = null;\n      DateTime createdAt = new DateTime(2012, 12, 12, 0, 0, 0, DateTimeKind.Utc);\n      using (TransactionScope scope = CreateTransactionScope())\n      {\n        UseConnections((_, connection) => {\n          jobId = connection.CreateExpiredJob(Job.FromExpression(() => SampleMethod(\"Hello\")),\n            new Dictionary<string, string> { { \"Key1\", \"Value1\" }, { \"Key2\", \"Value2\" } },\n            createdAt,\n            TimeSpan.FromDays(1));\n\n          Assert.NotNull(jobId);\n          Assert.NotEmpty(jobId);\n        });\n\n        if (completeTransactionScope)\n        {\n          scope.Complete();\n        }\n      }\n\n      UseConnections((connection, _) => {\n        if (completeTransactionScope)\n        {\n          dynamic sqlJob = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"job\"\"\");\n          Assert.Equal(jobId, sqlJob.id.ToString());\n          Assert.Equal(createdAt, sqlJob.createdat);\n          Assert.Null((long?)sqlJob.stateid);\n          Assert.Null((string)sqlJob.statename);\n        }\n        else\n        {\n          TestJob job = connection.QuerySingleOrDefault($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"job\"\"\");\n          Assert.Null(job);\n        }\n      });\n    }\n\n    private void UseConnections(Action<NpgsqlConnection, PostgreSqlConnection> action)\n    {\n      PostgreSqlStorage storage = _fixture.SafeInit();\n      action(storage.CreateAndOpenConnection(), storage.GetStorageConnection());\n    }\n\n    private void UseConnection(Action<PostgreSqlConnection> action)\n    {\n      PostgreSqlStorage storage = _fixture.SafeInit();\n      action(storage.GetStorageConnection());\n    }\n\n    private static void UseDisposableConnection(Action<PostgreSqlConnection> action)\n    {\n      using (NpgsqlConnection sqlConnection = ConnectionUtils.CreateConnection())\n      {\n        PostgreSqlStorageOptions options = new()\n        {\n          EnableTransactionScopeEnlistment = true,\n          SchemaName = GetSchemaName(),\n          TransactionSynchronisationTimeout = TimeSpan.FromSeconds(1),\n        };\n        PostgreSqlStorage storage = new(new ExistingNpgsqlConnectionFactory(sqlConnection, options), options);\n        using (PostgreSqlConnection connection = storage.GetStorageConnection())\n        {\n          action(connection);\n        }\n      }\n    }\n\n    private static string GetSchemaName()\n    {\n      return ConnectionUtils.GetSchemaName();\n    }\n\n#pragma warning disable xUnit1013 // Public method should be marked as test\n    public static void SampleMethod(string arg)\n#pragma warning restore xUnit1013 // Public method should be marked as test\n    { }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlDistributedLockFacts.cs",
    "content": "﻿using System;\nusing System.Data;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Moq;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlDistributedLockFacts : IDisposable\n  {\n    private readonly TimeSpan _timeout = TimeSpan.FromSeconds(5);\n    private NpgsqlConnection _connection;\n\n    public void Dispose()\n    {\n      _connection?.Dispose();\n    }\n\n    [Fact]\n    public void Acquire_ThrowsAnException_WhenResourceIsNullOrEmpty()\n    {\n      PostgreSqlStorageOptions options = new();\n\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(\n        () => PostgreSqlDistributedLock.Acquire(new Mock<IDbConnection>().Object, \"\", _timeout, options));\n\n      Assert.Equal(\"resource\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Acquire_ThrowsAnException_WhenConnectionIsNull()\n    {\n      PostgreSqlStorageOptions options = new();\n\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => PostgreSqlDistributedLock.Acquire(null, \"hello\", _timeout, options));\n\n      Assert.Equal(\"connection\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Acquire_ThrowsAnException_WhenOptionsIsNull()\n    {\n      Mock<IDbConnection> connection = new Mock<IDbConnection>();\n      connection.SetupGet(c => c.State).Returns(ConnectionState.Open);\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(\n        () => PostgreSqlDistributedLock.Acquire(new Mock<IDbConnection>().Object, \"hi\", _timeout, null));\n\n      Assert.Equal(\"options\", exception.ParamName);\n    }\n\n\n    [Fact]\n    [CleanDatabase]\n    public void Acquire_AcquiresExclusiveApplicationLock_WithUseNativeDatabaseTransactions_OnSession()\n    {\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = true,\n      };\n\n      UseConnection(connection => {\n        // ReSharper disable once UnusedVariable\n        PostgreSqlDistributedLock.Acquire(connection, \"hello\", _timeout, options);\n\n        long lockCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"lock\"\" WHERE \"\"resource\"\" = @Resource\",\n          new { Resource = \"hello\" });\n\n        Assert.Equal(1, lockCount);\n        //Assert.Equal(\"Exclusive\", lockMode);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Acquire_AcquiresExclusiveApplicationLock_WithUseNativeDatabaseTransactions_OnSession_WhenDeadlockOccurs()\n    {\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = true,\n        DistributedLockTimeout = TimeSpan.FromSeconds(10),\n      };\n\n      UseConnection(connection => {\n        // Arrange\n        TimeSpan timeout = TimeSpan.FromSeconds(15);\n        string resourceName = \"hello\";\n        connection.Execute($@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"lock\"\" VALUES (@ResourceName, 0, @Now)\", new { ResourceName = resourceName, Now = DateTime.UtcNow });\n\n        // Act && Assert (not throwing means it worked)\n        PostgreSqlDistributedLock.Acquire(connection, resourceName, timeout, options);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Acquire_AcquiresExclusiveApplicationLock_WithoutUseNativeDatabaseTransactions_OnSession()\n    {\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = false,\n      };\n\n      UseConnection(connection => {\n        // Acquire locks on two different resources to make sure they don't conflict.\n        PostgreSqlDistributedLock.Acquire(connection, \"hello\", _timeout, options);\n        PostgreSqlDistributedLock.Acquire(connection, \"hello2\", _timeout, options);\n\n        long lockCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"lock\"\" WHERE \"\"resource\"\" = @Resource\",\n          new { Resource = \"hello\" });\n\n        Assert.Equal(1, lockCount);\n      });\n    }\n\n\n    [Fact]\n    [CleanDatabase]\n    public void Acquire_ThrowsAnException_IfLockCanNotBeGranted_WithUseNativeDatabaseTransactions()\n    {\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = true,\n      };\n\n      ManualResetEventSlim releaseLock = new(false);\n      ManualResetEventSlim lockAcquired = new(false);\n\n      Thread thread = new(() => UseConnection(connection1 => {\n        PostgreSqlDistributedLock.Acquire(connection1, \"exclusive\", _timeout, options);\n        lockAcquired.Set();\n        releaseLock.Wait();\n        PostgreSqlDistributedLock.Release(connection1, \"exclusive\", options);\n      }));\n      thread.Start();\n\n      lockAcquired.Wait();\n\n      UseConnection(connection2 =>\n        Assert.Throws<PostgreSqlDistributedLockException>(() => PostgreSqlDistributedLock.Acquire(connection2, \"exclusive\", _timeout, options)));\n\n      releaseLock.Set();\n      thread.Join();\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Acquire_ThrowsAnException_IfLockCanNotBeGranted_WithoutUseNativeDatabaseTransactions()\n    {\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = false,\n      };\n\n      ManualResetEventSlim releaseLock = new(false);\n      ManualResetEventSlim lockAcquired = new(false);\n\n      Thread thread = new(() => UseConnection(connection1 => {\n        PostgreSqlDistributedLock.Acquire(connection1, \"exclusive\", _timeout, options);\n        lockAcquired.Set();\n        releaseLock.Wait();\n        PostgreSqlDistributedLock.Release(connection1, \"exclusive\", options);\n      }));\n      thread.Start();\n\n      lockAcquired.Wait();\n\n      UseConnection(connection2 =>\n        Assert.Throws<PostgreSqlDistributedLockException>(() => PostgreSqlDistributedLock.Acquire(connection2, \"exclusive\", _timeout, options)));\n\n      releaseLock.Set();\n      thread.Join();\n    }\n\n    [Theory]\n    [InlineData(true)]\n    [InlineData(false)]\n    [CleanDatabase]\n    public void Acquire_ExpiredLockExists_LocksAnyway(bool useNativeDatabaseTransactions)\n    {\n      const string resource = \"hello\";\n\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = useNativeDatabaseTransactions,\n      };\n\n      UseConnection(connection => {\n        DateTime acquired = DateTime.UtcNow - options.DistributedLockTimeout - TimeSpan.FromMinutes(1);\n        connection.Execute($@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"lock\"\" (\"\"resource\"\", \"\"acquired\"\") VALUES (@Resource, @Acquired)\", new { Resource = resource, Acquired = acquired });\n\n        PostgreSqlDistributedLock.Acquire(connection, resource, _timeout, options);\n\n        long lockCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"lock\"\" WHERE \"\"resource\"\" = @Resource\",\n          new { Resource = resource });\n\n        Assert.Equal(1, lockCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dispose_ReleasesExclusiveApplicationLock_WithUseNativeDatabaseTransactions()\n    {\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = true,\n      };\n\n      UseConnection(connection => {\n        PostgreSqlDistributedLock.Acquire(connection, \"hello\", _timeout, options);\n        PostgreSqlDistributedLock.Release(connection, \"hello\", options);\n\n        long lockCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"lock\"\" WHERE \"\"resource\"\" = @Resource\",\n          new { Resource = \"hello\" });\n\n        Assert.Equal(0, lockCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dispose_ReleasesExclusiveApplicationLock_WithoutUseNativeDatabaseTransactions()\n    {\n      PostgreSqlStorageOptions options = new() {\n        SchemaName = GetSchemaName(),\n        UseNativeDatabaseTransactions = false,\n      };\n\n      UseConnection(connection => {\n        PostgreSqlDistributedLock.Acquire(connection, \"hello\", _timeout, options);\n        PostgreSqlDistributedLock.Release(connection, \"hello\", options);\n\n        long lockCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"lock\"\" WHERE \"\"resource\"\" = @Resource\",\n          new { Resource = \"hello\" });\n\n        Assert.Equal(0, lockCount);\n      });\n    }\n\n    private void UseConnection(Action<NpgsqlConnection> action)\n    {\n      _connection ??= ConnectionUtils.CreateConnection();\n      action(_connection);\n    }\n\n    private static string GetSchemaName()\n    {\n      return ConnectionUtils.GetSchemaName();\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlFetchedJobFacts.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlFetchedJobFacts\n  {\n    private const string JobId = \"id\";\n    private const string Queue = \"queue\";\n    private DateTime _fetchedAt = DateTime.UtcNow; \n\n    private readonly PostgreSqlStorage _storage;\n\n    public PostgreSqlFetchedJobFacts()\n    {\n      _storage = new PostgreSqlStorage(ConnectionUtils.GetDefaultConnectionFactory());\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenStorageIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlFetchedJob(null, 1, JobId, Queue, _fetchedAt));\n\n      Assert.Equal(\"storage\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenJobIdIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlFetchedJob(_storage, 1, null, Queue, _fetchedAt));\n\n      Assert.Equal(\"jobId\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenQueueIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlFetchedJob(_storage, 1, JobId, null, _fetchedAt));\n\n      Assert.Equal(\"queue\", exception.ParamName);\n    }\n    \n    [Fact]\n    public void Ctor_ThrowsAnException_WhenFetchedAtIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlFetchedJob(_storage, 1, JobId, Queue, null));\n      Assert.Equal(\"fetchedAt\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Ctor_CorrectlySets_AllInstanceProperties()\n    {\n      PostgreSqlFetchedJob fetchedJob = new(_storage, 1, JobId, Queue, _fetchedAt);\n\n      Assert.Equal(1, fetchedJob.Id);\n      Assert.Equal(JobId, fetchedJob.JobId);\n      Assert.Equal(Queue, fetchedJob.Queue);\n      Assert.Equal(_fetchedAt, fetchedJob.FetchedAt);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromQueue_ReallyDeletesTheJobFromTheQueue()\n    {\n      // Arrange\n      long id = CreateJobQueueRecord(_storage, \"1\", \"default\", _fetchedAt);\n      PostgreSqlFetchedJob processingJob = new(_storage, id, \"1\", \"default\", _fetchedAt);\n\n      // Act\n      processingJob.RemoveFromQueue();\n\n      // Assert\n      long count = _storage.UseConnection(null, connection =>\n        connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\"));\n      Assert.Equal(0, count);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromQueue_DoesNotDelete_UnrelatedJobs()\n    {\n      // Arrange\n      CreateJobQueueRecord(_storage, \"1\", \"default\", _fetchedAt);\n      CreateJobQueueRecord(_storage, \"1\", \"critical\", _fetchedAt);\n      CreateJobQueueRecord(_storage, \"2\", \"default\", _fetchedAt);\n\n      PostgreSqlFetchedJob fetchedJob = new PostgreSqlFetchedJob(_storage, 999, \"1\", \"default\", _fetchedAt);\n\n      // Act\n      fetchedJob.RemoveFromQueue();\n\n      // Assert\n      long count = _storage.UseConnection(null, connection =>\n        connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\"));\n      Assert.Equal(3, count);\n    }\n    \n    [Fact]\n    [CleanDatabase]\n    public void Requeue_SetsFetchedAtValueToNull()\n    {\n      // Arrange\n      long id = CreateJobQueueRecord(_storage, \"1\", \"default\", _fetchedAt);\n      PostgreSqlFetchedJob processingJob = new(_storage, id, \"1\", \"default\", _fetchedAt);\n\n      // Act\n      processingJob.Requeue();\n\n      // Assert\n      dynamic record = _storage.UseConnection(null, connection =>\n        connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\"));\n      Assert.Null(record.fetchedat);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Timer_UpdatesFetchedAtColumn()\n    {\n      _storage.UseConnection(null, connection => {\n        // Arrange\n        var fetchedAt = DateTime.UtcNow.AddMinutes(-5);\n        long id = CreateJobQueueRecord(_storage, \"1\", \"default\", fetchedAt);\n        using (var processingJob = new PostgreSqlFetchedJob(_storage, id, \"1\", \"default\", fetchedAt))\n        {\n          processingJob.DisposeTimer();\n          Thread.Sleep(TimeSpan.FromSeconds(10));\n          processingJob.ExecuteKeepAliveQueryIfRequired();\n\n          dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\");\n\n          Assert.NotNull(processingJob.FetchedAt);\n          Assert.Equal<DateTime?>(processingJob.FetchedAt, record.fetchedat);\n          DateTime now = DateTime.UtcNow;\n          Assert.True(now.AddSeconds(-5) < record.fetchedat, (now - record.fetchedat).ToString());\n        }\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromQueue_AfterTimer_RemovesJobFromTheQueue()\n    {\n      _storage.UseConnection(null, connection => {\n        // Arrange\n        long id = CreateJobQueueRecord(_storage, \"1\", \"default\", _fetchedAt);\n        using (PostgreSqlFetchedJob processingJob = new PostgreSqlFetchedJob(_storage, id, \"1\", \"default\", _fetchedAt))\n        {\n          Thread.Sleep(TimeSpan.FromSeconds(10));\n          processingJob.DisposeTimer();\n\n          // Act\n          processingJob.RemoveFromQueue();\n\n          // Assert\n          int count = connection.QuerySingle<int>($@\"SELECT count(*) FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\");\n          Assert.Equal(0, count);\n        }\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RequeueQueue_AfterTimer_SetsFetchedAtValueToNull()\n    {\n      _storage.UseConnection(null, connection => {\n        // Arrange\n        long id = CreateJobQueueRecord(_storage, \"1\", \"default\", _fetchedAt);\n        using (var processingJob = new PostgreSqlFetchedJob(_storage, id, \"1\", \"default\", _fetchedAt))\n        {\n          Thread.Sleep(TimeSpan.FromSeconds(10));\n          processingJob.DisposeTimer();\n\n          // Act\n          processingJob.Requeue();\n\n          // Assert\n          dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\");\n          Assert.Null(record.fetchedat);\n        }\n      });\n    }\n    \n    [Fact]\n    [CleanDatabase]\n    public void Dispose_SetsFetchedAtValueToNull_IfThereWereNoCallsToComplete()\n    {\n      // Arrange\n      long id = CreateJobQueueRecord(_storage, \"1\", \"default\", _fetchedAt);\n      PostgreSqlFetchedJob processingJob = new(_storage, id, \"1\", \"default\", _fetchedAt);\n\n      // Act\n      processingJob.Dispose();\n\n      // Assert\n      dynamic record = _storage.UseConnection(null, connection =>\n        connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\"));\n      Assert.Null(record.fetchedat);\n    }\n\n    private static long CreateJobQueueRecord(PostgreSqlStorage storage, string jobId, string queue, DateTime? fetchedAt)\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\", \"\"fetchedat\"\")\n        VALUES (@Id, @Queue, @FetchedAt) RETURNING \"\"id\"\"\n      \";\n\n      return\n        storage.UseConnection(null, connection =>\n          connection.QuerySingle<long>(arrangeSql, \n            new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture), Queue = queue, FetchedAt = fetchedAt }));\n    }\n\n    private static string GetSchemaName()\n    {\n      return ConnectionUtils.GetSchemaName();\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlInstallerFacts.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing Dapper;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlInstallerFacts\n  {\n    [Fact]\n    public void InstallingSchemaUpdatesVersionAndShouldNotThrowAnException()\n    {\n      Exception ex = Record.Exception(() => {\n        UseConnection(connection => {\n          string schemaName = \"hangfire_tests_\" + Guid.NewGuid().ToString().Replace(\"-\", \"_\").ToLower(CultureInfo.InvariantCulture);\n\n          PostgreSqlObjectsInstaller.Install(connection, schemaName);\n\n          int lastVersion = connection.QuerySingle<int>($@\"SELECT version FROM \"\"{schemaName}\"\".\"\"schema\"\"\");\n          Assert.Equal(23, lastVersion);\n\n          connection.Execute($@\"DROP SCHEMA \"\"{schemaName}\"\" CASCADE;\");\n        });\n      });\n\n      Assert.Null(ex);\n    }\n\n    [Fact]\n    public void InstallingSchemaWithCapitalsUpdatesVersionAndShouldNotThrowAnException()\n    {\n      Exception ex = Record.Exception(() => {\n        UseConnection(connection => {\n          string schemaName = \"Hangfire_Tests_\" + Guid.NewGuid().ToString().Replace(\"-\", \"_\").ToLower(CultureInfo.InvariantCulture);\n\n          PostgreSqlObjectsInstaller.Install(connection, schemaName);\n\n          int lastVersion = connection.QuerySingle<int>($@\"SELECT version FROM \"\"{schemaName}\"\".\"\"schema\"\"\");\n          Assert.Equal(23, lastVersion);\n\n          connection.Execute($@\"DROP SCHEMA \"\"{schemaName}\"\" CASCADE;\");\n        });\n      });\n\n      Assert.Null(ex);\n    }\n\n    private static void UseConnection(Action<NpgsqlConnection> action)\n    {\n      using NpgsqlConnection connection = ConnectionUtils.CreateConnection();\n      action(connection);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlJobQueueFacts.cs",
    "content": "﻿using System;\nusing System.Data;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Dapper;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Hangfire.PostgreSql.Utils;\nusing Hangfire.Storage;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlJobQueueFacts : IClassFixture<PostgreSqlStorageFixture>\n  {\n    private static readonly string[] _defaultQueues = { \"default\" };\n\n    private readonly PostgreSqlStorageFixture _fixture;\n\n    public PostgreSqlJobQueueFacts(PostgreSqlStorageFixture fixture)\n    {\n      _fixture = fixture;\n      _fixture.SetupOptions(o => o.UseSlidingInvisibilityTimeout = true);\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenStorageIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlJobQueue(null));\n\n      Assert.Equal(\"storage\", exception.ParamName);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsNull()\n    {\n      UseConnection((_, storage) => {\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, false);\n\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => queue.Dequeue(null, CreateTimingOutCancellationToken()));\n\n        Assert.Equal(\"queues\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchAJob_FromQueueWithHigherPriority()\n    {\n      UseConnection((connection, storage) => {\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, false);\n        CancellationToken token = CreateTimingOutCancellationToken();\n\n        queue.Enqueue(connection, \"1\", \"1\");\n        queue.Enqueue(connection, \"2\", \"2\");\n        queue.Enqueue(connection, \"3\", \"3\");\n\n        Assert.Equal(\"1\", queue.Dequeue(new[] { \"1\", \"2\", \"3\" }, token).JobId);\n        Assert.Equal(\"2\", queue.Dequeue(new[] { \"2\", \"3\", \"1\" }, token).JobId);\n        Assert.Equal(\"3\", queue.Dequeue(new[] { \"3\", \"1\", \"2\" }, token).JobId);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    private void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsEmpty_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsEmpty(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    private void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsEmpty_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsEmpty(false);\n    }\n\n    private void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsEmpty(bool useNativeDatabaseTransactions)\n    {\n      UseConnection((_, storage) => {\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        ArgumentException exception = Assert.Throws<ArgumentException>(() => queue.Dequeue(Array.Empty<string>(), CreateTimingOutCancellationToken()));\n\n        Assert.Equal(\"queues\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    private void\n      Dequeue_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning(true);\n    }\n\n    [Fact]\n    private void\n      Dequeue_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning(false);\n    }\n\n    private void Dequeue_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning(\n      bool useNativeDatabaseTransactions)\n    {\n      UseConnection((_, storage) => {\n        CancellationTokenSource cts = new CancellationTokenSource();\n        cts.Cancel();\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        Assert.Throws<OperationCanceledException>(() => queue.Dequeue(_defaultQueues, cts.Token));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldWaitIndefinitely_WhenThereAreNoJobs_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldWaitIndefinitely_WhenThereAreNoJobs(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldWaitIndefinitely_WhenThereAreNoJobs_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldWaitIndefinitely_WhenThereAreNoJobs(false);\n    }\n\n    private void Dequeue_ShouldWaitIndefinitely_WhenThereAreNoJobs(bool useNativeDatabaseTransactions)\n    {\n      UseConnection((_, storage) => {\n        CancellationTokenSource cts = new CancellationTokenSource(200);\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        Assert.Throws<OperationCanceledException>(() => queue.Dequeue(_defaultQueues, cts.Token));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchAJob_FromTheSpecifiedQueue_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchAJob_FromTheSpecifiedQueue(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchAJob_FromTheSpecifiedQueue_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchAJob_FromTheSpecifiedQueue(false);\n    }\n\n    private void Dequeue_ShouldFetchAJob_FromTheSpecifiedQueue(bool useNativeDatabaseTransactions)\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\")\n        VALUES (@JobId, @Queue) RETURNING \"\"id\"\"\n      \";\n\n      // Arrange\n      UseConnection((connection, storage) => {\n        long id = connection.QuerySingle<long>(arrangeSql,\n          new { JobId = 1, Queue = \"default\" });\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        // Act\n        PostgreSqlFetchedJob payload = (PostgreSqlFetchedJob)queue.Dequeue(_defaultQueues,\n          CreateTimingOutCancellationToken());\n\n        // Assert\n        Assert.Equal(id, payload.Id);\n        Assert.Equal(\"1\", payload.JobId);\n        Assert.Equal(\"default\", payload.Queue);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldLeaveJobInTheQueue_ButSetItsFetchedAtValue_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldLeaveJobInTheQueue_ButSetItsFetchedAtValue(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldLeaveJobInTheQueue_ButSetItsFetchedAtValue_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldLeaveJobInTheQueue_ButSetItsFetchedAtValue(false);\n    }\n\n    private void Dequeue_ShouldLeaveJobInTheQueue_ButSetItsFetchedAtValue(bool useNativeDatabaseTransactions)\n    {\n      string arrangeSql = $@\"\n        WITH i AS (\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n          VALUES (@InvocationData::jsonb, @Arguments::jsonb, NOW())\n          RETURNING \"\"id\"\"\n        )\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\")\n        SELECT i.\"\"id\"\", @Queue FROM i;\n      \";\n\n      // Arrange\n      UseConnection((connection, storage) => {\n        connection.Execute(arrangeSql,\n          new { InvocationData = JsonParameter.GetParameterValue(\"\"), Arguments = JsonParameter.GetParameterValue(\"\", JsonParameter.ValueType.Array), Queue = \"default\" });\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        // Act\n        IFetchedJob payload = queue.Dequeue(_defaultQueues,\n          CreateTimingOutCancellationToken());\n\n        // Assert\n        Assert.NotNull(payload);\n\n        DateTime? fetchedAt = connection.QuerySingle<DateTime?>($@\"SELECT \"\"fetchedat\"\" FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" WHERE \"\"jobid\"\" = @Id\",\n          new { Id = Convert.ToInt64(payload.JobId, CultureInfo.InvariantCulture) });\n\n        Assert.NotNull(fetchedAt);\n        Assert.True(fetchedAt > DateTime.UtcNow.AddMinutes(-1));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue(true, false);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue(false, false);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue_WithUseNativeDatabaseTransactionsAndSlidingInvisbility()\n    {\n      Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue(true, true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue_WithoutUseNativeDatabaseTransactionsAndSlidingInvisbility()\n    {\n      Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue(false, true);\n    }\n    \n    private void Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue(bool useNativeDatabaseTransactions, bool useSlidingInvisibilityTimeout)\n    {\n      string arrangeSql = $@\"\n        WITH i AS (\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n          VALUES (@InvocationData::jsonb, @Arguments::jsonb, NOW())\n          RETURNING \"\"id\"\"\n        )\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\", \"\"fetchedat\"\")\n        SELECT i.\"\"id\"\", @Queue, @FetchedAt \n        FROM i;\n      \";\n\n      // Arrange\n      UseConnection((connection, storage) => {\n        connection.Execute(arrangeSql,\n          new {\n            Queue = \"default\",\n            FetchedAt = DateTime.UtcNow.AddDays(-1),\n            InvocationData = JsonParameter.GetParameterValue(\"\"),\n            Arguments = JsonParameter.GetParameterValue(\"\", JsonParameter.ValueType.Array),\n          });\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions, useSlidingInvisibilityTimeout: useSlidingInvisibilityTimeout);\n\n        // Act\n        IFetchedJob payload = queue.Dequeue(_defaultQueues,\n          CreateTimingOutCancellationToken());\n\n        // Assert\n        Assert.NotEmpty(payload.JobId);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldSetFetchedAt_OnlyForTheFetchedJob_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldSetFetchedAt_OnlyForTheFetchedJob(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldSetFetchedAt_OnlyForTheFetchedJob_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldSetFetchedAt_OnlyForTheFetchedJob(false);\n    }\n\n    private void Dequeue_ShouldSetFetchedAt_OnlyForTheFetchedJob(bool useNativeDatabaseTransactions)\n    {\n      string arrangeSql = $@\"\n        WITH i AS (\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n          VALUES (@InvocationData::jsonb, @Arguments::jsonb, NOW())\n          RETURNING \"\"id\"\"\n        )\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\")\n        SELECT i.\"\"id\"\", @Queue FROM i;\n      \";\n\n      UseConnection((connection, storage) => {\n        connection.Execute(arrangeSql,\n          new[] {\n            new { Queue = \"default\", InvocationData = JsonParameter.GetParameterValue(\"\"), Arguments = JsonParameter.GetParameterValue(\"\", JsonParameter.ValueType.Array) },\n            new { Queue = \"default\", InvocationData = JsonParameter.GetParameterValue(\"\"), Arguments = JsonParameter.GetParameterValue(\"\", JsonParameter.ValueType.Array) },\n          });\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        // Act\n        IFetchedJob payload = queue.Dequeue(_defaultQueues,\n          CreateTimingOutCancellationToken());\n\n        // Assert\n        DateTime? otherJobFetchedAt = connection.QuerySingle<DateTime?>($@\"SELECT \"\"fetchedat\"\" FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" WHERE \"\"jobid\"\" <> @Id\",\n          new { Id = Convert.ToInt64(payload.JobId, CultureInfo.InvariantCulture) });\n\n        Assert.Null(otherJobFetchedAt);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchJobs_OnlyFromSpecifiedQueues_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchJobs_OnlyFromSpecifiedQueues(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldFetchJobs_OnlyFromSpecifiedQueues_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchJobs_OnlyFromSpecifiedQueues(false);\n    }\n\n\n    private void Dequeue_ShouldFetchJobs_OnlyFromSpecifiedQueues(bool useNativeDatabaseTransactions)\n    {\n      string arrangeSql = $@\"\n        WITH i AS (\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n          VALUES (@InvocationData::jsonb, @Arguments::jsonb, NOW())\n          RETURNING \"\"id\"\"\n        )\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\")\n        SELECT i.\"\"id\"\", @Queue FROM i;\n      \";\n      UseConnection((connection, storage) => {\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        connection.Execute(arrangeSql,\n          new { Queue = \"critical\", InvocationData = JsonParameter.GetParameterValue(\"\"), Arguments = JsonParameter.GetParameterValue(\"\", JsonParameter.ValueType.Array) });\n\n        Assert.Throws<OperationCanceledException>(() => queue.Dequeue(_defaultQueues,\n          CreateTimingOutCancellationToken()));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    private void Dequeue_ShouldFetchJobs_FromMultipleQueues_WithUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchJobs_FromMultipleQueues(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    private void Dequeue_ShouldFetchJobs_FromMultipleQueues_WithoutUseNativeDatabaseTransactions()\n    {\n      Dequeue_ShouldFetchJobs_FromMultipleQueues(false);\n    }\n\n    private void Dequeue_ShouldFetchJobs_FromMultipleQueues(bool useNativeDatabaseTransactions)\n    {\n      string arrangeSql = $@\"\n        WITH i AS (\n          INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n          VALUES (@InvocationData::jsonb, @Arguments::jsonb, NOW())\n          RETURNING \"\"id\"\"\n        )\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\")\n        SELECT i.\"\"id\"\", @Queue FROM i;\n      \";\n\n      string[] queueNames = { \"default\", \"critical\" };\n\n      UseConnection((connection, storage) => {\n        connection.Execute(arrangeSql,\n          new[] {\n            new { Queue = queueNames.First(), InvocationData = JsonParameter.GetParameterValue(\"\") , Arguments = JsonParameter.GetParameterValue(\"\", JsonParameter.ValueType.Array) },\n            new { Queue = queueNames.Last(), InvocationData = JsonParameter.GetParameterValue(\"\"), Arguments = JsonParameter.GetParameterValue(\"\", JsonParameter.ValueType.Array) },\n          });\n\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        PostgreSqlFetchedJob queueFirst = (PostgreSqlFetchedJob)queue.Dequeue(queueNames,\n          CreateTimingOutCancellationToken());\n\n        Assert.NotNull(queueFirst.JobId);\n        Assert.Contains(queueFirst.Queue, queueNames);\n\n        PostgreSqlFetchedJob queueLast = (PostgreSqlFetchedJob)queue.Dequeue(queueNames,\n          CreateTimingOutCancellationToken());\n\n        Assert.NotNull(queueLast.JobId);\n        Assert.Contains(queueLast.Queue, queueNames);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Enqueue_AddsAJobToTheQueue_WithUseNativeDatabaseTransactions()\n    {\n      Enqueue_AddsAJobToTheQueue(true);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Enqueue_AddsAJobToTheQueue_WithoutUseNativeDatabaseTransactions()\n    {\n      Enqueue_AddsAJobToTheQueue(false);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Queues_Should_Support_Long_Queue_Names()\n    {\n      UseConnection((connection, storage) => {\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, false);\n\n        string name = \"very_long_name_that_is_over_20_characters_long_or_something\";\n\n        Assert.True(name.Length > 21);\n\n        queue.Enqueue(connection, name, \"1\");\n\n        string retrievedName = connection.QuerySingle<string>($@\"SELECT \"\"queue\"\" FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\");\n        Assert.Equal(name, retrievedName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Queues_Can_Dequeue_On_Signal()\n    {\n      UseConnection((connection, storage) => {\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, false);\n        IFetchedJob job = null;\n        //as UseConnection does not support async-await we have to work with Thread.Sleep\n\n        Task.Run(() => {\n          //dequeue the job asynchronously\n          job = queue.Dequeue(new[] { \"default\" }, CreateTimingOutCancellationToken());\n        });\n        //all sleeps are possibly way to high but this ensures that any race condition is unlikely\n        //to ensure that the task would run \n        Thread.Sleep(1000);\n        Assert.Null(job);\n        //enqueue a job that does not trigger the existing queue to reevaluate its state\n        queue.Enqueue(connection, \"default\", \"1\");\n        Thread.Sleep(1000);\n        //the job should still be unset\n        Assert.Null(job);\n        //trigger a reevaluation\n        queue.FetchNextJob();\n        //wait for the Dequeue to execute and return the next job\n        Thread.Sleep(1000);\n        Assert.NotNull(job);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Queues_Can_Dequeue_On_Notification()\n    {\n      UseConnection((connection, storage) => {\n        TimeSpan timeout = TimeSpan.FromSeconds(30);\n\n        // Only for Postgres 11+ should we have a polling time greater than the timeout.\n        if (connection.SupportsNotifications())\n        {\n          storage.Options.QueuePollInterval = TimeSpan.FromMinutes(2);\n        }\n\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, false, true);\n        IFetchedJob job = null;\n        //as UseConnection does not support async-await we have to work with Thread.Sleep\n\n        Task task = Task.Run(() => {\n          //dequeue the job asynchronously\n          CancellationTokenSource cancellationTokenSource = new(timeout);\n          try\n          {\n            job = queue.Dequeue(new[] { \"default\" }, cancellationTokenSource.Token);\n          }\n          catch (OperationCanceledException)\n          {\n            // Do nothing, task was intentionally cancelled.\n          }\n          finally\n          {\n            cancellationTokenSource.Dispose();\n          }\n        });\n\n        Thread.Sleep(2000); // Give thread time to startup.\n\n        queue.Enqueue(connection, \"default\", \"1\");\n\n        task.Wait(timeout);\n\n        Assert.NotNull(job);\n      });\n    }\n\n    private void Enqueue_AddsAJobToTheQueue(bool useNativeDatabaseTransactions)\n    {\n      UseConnection((connection, storage) => {\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, useNativeDatabaseTransactions);\n\n        queue.Enqueue(connection, \"default\", \"1\");\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\");\n        Assert.Equal(\"1\", record.jobid.ToString());\n        Assert.Equal(\"default\", record.queue);\n        Assert.Null(record.FetchedAt);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Dequeue_ShouldSelfHeal_WhenListenConnectionFails()\n    {\n      UseConnection((_, storage) => {\n        storage.Options.QueuePollInterval = TimeSpan.FromMilliseconds(500);\n        PostgreSqlJobQueue queue = CreateJobQueue(storage, false, true);\n        Exception thrownException = null;\n        IFetchedJob job = null;\n\n        CancellationTokenSource cts = new(TimeSpan.FromSeconds(10));\n\n        Task dequeueTask = Task.Run(() => {\n          try\n          {\n            job = queue.Dequeue(new[] { \"default\" }, cts.Token);\n          }\n          catch (Exception ex) when (ex is not OperationCanceledException)\n          {\n            thrownException = ex;\n          }\n        });\n\n        Thread.Sleep(1000);\n\n        using (NpgsqlConnection adminConnection = ConnectionUtils.CreateMasterConnection())\n        {\n          adminConnection.Execute(@\"\n            SELECT pg_terminate_backend(pid)\n            FROM pg_stat_activity\n            WHERE query LIKE '%LISTEN%'\n            AND pid <> pg_backend_pid()\");\n        }\n\n        Thread.Sleep(500);\n\n        using (NpgsqlConnection enqueueConnection = ConnectionUtils.CreateConnection())\n        {\n          queue.Enqueue(enqueueConnection, \"default\", \"1\");\n        }\n\n        dequeueTask.Wait(TimeSpan.FromSeconds(5));\n\n        Assert.Null(thrownException);\n        Assert.NotNull(job);\n      });\n    }\n\n    private static CancellationToken CreateTimingOutCancellationToken()\n    {\n      CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromSeconds(10));\n      return source.Token;\n    }\n\n#pragma warning disable xUnit1013 // Public method should be marked as test\n    public static void Sample(string arg1, string arg2)\n#pragma warning restore xUnit1013 // Public method should be marked as test\n    { }\n\n    private static PostgreSqlJobQueue CreateJobQueue(PostgreSqlStorage storage, bool useNativeDatabaseTransactions, bool enableLongPolling = false, bool useSlidingInvisibilityTimeout = false)\n    {\n      storage.Options.SchemaName = GetSchemaName();\n      storage.Options.UseNativeDatabaseTransactions = useNativeDatabaseTransactions;\n      storage.Options.EnableLongPolling = enableLongPolling;\n      storage.Options.UseSlidingInvisibilityTimeout = useSlidingInvisibilityTimeout;\n\n      return new PostgreSqlJobQueue(storage);\n    }\n\n    private void UseConnection(Action<IDbConnection, PostgreSqlStorage> action)\n    {\n      PostgreSqlStorage storage = _fixture.SafeInit();\n      storage.UseConnection(null, connection => {\n        action(connection, storage);\n\n        return true;\n      });\n    }\n\n    private static string GetSchemaName()\n    {\n      return ConnectionUtils.GetSchemaName();\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlMonitoringApiFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Hangfire.Storage.Monitoring;\nusing Moq;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlMonitoringApiFacts : IClassFixture<PostgreSqlStorageFixture>\n  {\n    private readonly PostgreSqlStorageFixture _fixture;\n\n    public PostgreSqlMonitoringApiFacts(PostgreSqlStorageFixture fixture)\n    {\n      _fixture = fixture;\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetJobs_MixedCasing_ReturnsJob()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{ConnectionUtils.GetSchemaName()}\"\".\"\"job\"\"(\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES (@InvocationData::jsonb, @Arguments::jsonb, NOW()) RETURNING \"\"id\"\"\";\n\n      Job job = Job.FromExpression(() => SampleMethod(\"Hello\"));\n      InvocationData invocationData = InvocationData.SerializeJob(job);\n\n      UseConnection(connection => {\n        long jobId = connection.QuerySingle<long>(arrangeSql,\n          new {\n            InvocationData = JsonParameter.GetParameterValue(SerializationHelper.Serialize(invocationData)),\n            Arguments = JsonParameter.GetParameterValue(invocationData.Arguments, JsonParameter.ValueType.Array),\n          });\n\n        Mock<IState> state = new();\n        state.Setup(x => x.Name).Returns(SucceededState.StateName);\n        state.Setup(x => x.SerializeData())\n          .Returns(new Dictionary<string, string> {\n            { \"SUCCEEDEDAT\", \"2018-05-03T13:28:18.3939693Z\" },\n            { \"PerformanceDuration\", \"53\" },\n            { \"latency\", \"6730\" },\n          });\n\n        Commit(connection, x => x.SetJobState(jobId.ToString(CultureInfo.InvariantCulture), state.Object));\n\n        IMonitoringApi monitoringApi = _fixture.Storage.GetMonitoringApi();\n        JobList<SucceededJobDto> jobs = monitoringApi.SucceededJobs(0, 10);\n\n        Assert.NotNull(jobs);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void HourlySucceededJobs_ReturnsAggregatedStats()\n    {\n      DateTime now = DateTime.UtcNow;\n      string schemaName = ConnectionUtils.GetSchemaName();\n      string key = $\"stats:succeeded:{now.ToString(\"yyyy-MM-dd-HH\", CultureInfo.InvariantCulture)}\";\n      string arrangeSql =\n        $\"\"\"\n        BEGIN;\n        INSERT INTO \"{schemaName}\".\"counter\"(\"key\", \"value\") \n        VALUES (@Key, 5);\n        INSERT INTO \"{schemaName}\".\"aggregatedcounter\"(\"key\", \"value\") \n        VALUES (@Key, 7);\n        COMMIT;\n        \"\"\";\n      UseConnection(connection => {\n        connection.Execute(arrangeSql, new { Key = key });\n\n        IMonitoringApi monitoringApi = _fixture.Storage.GetMonitoringApi();\n        IDictionary<DateTime, long> stats = monitoringApi.HourlySucceededJobs();\n        Assert.Equal(24, stats.Count);\n\n        long actualCounter = Assert.Single(stats.Where(x => x.Key.Hour == now.Hour).Select(x => x.Value));\n        Assert.Equal(12, actualCounter);\n      });\n    }\n\n    private void UseConnection(Action<NpgsqlConnection> action)\n    {\n      PostgreSqlStorage storage = _fixture.SafeInit();\n      action(storage.CreateAndOpenConnection());\n    }\n\n    private void Commit(\n      NpgsqlConnection connection,\n      Action<PostgreSqlWriteOnlyTransaction> action)\n    {\n      PostgreSqlStorage storage = _fixture.SafeInit();\n      using PostgreSqlWriteOnlyTransaction transaction = new(storage, () => connection);\n      action(transaction);\n      transaction.Commit();\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void FetchedJobs_WithDuplicateJobQueueEntries_DoesNotThrow()\n    {\n      string schemaName = ConnectionUtils.GetSchemaName();\n\n      string createJobSql = $@\"\n        INSERT INTO \"\"{schemaName}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES (@InvocationData::jsonb, @Arguments::jsonb, NOW()) RETURNING \"\"id\"\"\";\n\n      string createJobQueueSql = $@\"\n        INSERT INTO \"\"{schemaName}\"\".\"\"jobqueue\"\" (\"\"jobid\"\", \"\"queue\"\", \"\"fetchedat\"\")\n        VALUES (@JobId, @Queue, @FetchedAt)\";\n\n      UseConnection(connection => {\n        Job job = Job.FromExpression(() => SampleMethod(\"test\"));\n        InvocationData invocationData = InvocationData.SerializeJob(job);\n\n        long jobId = connection.QuerySingle<long>(createJobSql,\n          new {\n            InvocationData = JsonParameter.GetParameterValue(SerializationHelper.Serialize(invocationData)),\n            Arguments = JsonParameter.GetParameterValue(invocationData.Arguments, JsonParameter.ValueType.Array),\n          });\n\n        DateTime fetchedAt = DateTime.UtcNow;\n        connection.Execute(createJobQueueSql, new { JobId = jobId, Queue = \"default\", FetchedAt = fetchedAt });\n        connection.Execute(createJobQueueSql, new { JobId = jobId, Queue = \"default\", FetchedAt = fetchedAt.AddSeconds(1) });\n\n        PostgreSqlStorage storage = _fixture.SafeInit();\n        PostgreSqlStorageOptions options = new() { SchemaName = schemaName };\n\n        PostgreSqlJobQueueProvider provider = new(storage, options);\n        PersistentJobQueueProviderCollection providers = new(provider);\n        storage.QueueProviders = providers;\n\n        IMonitoringApi monitoringApi = storage.GetMonitoringApi();\n        JobList<FetchedJobDto> fetchedJobs = monitoringApi.FetchedJobs(\"default\", 0, 10);\n\n        Assert.NotNull(fetchedJobs);\n        Assert.Single(fetchedJobs);\n      });\n    }\n\n#pragma warning disable xUnit1013 // Public method should be marked as test\n    public static void SampleMethod(string arg)\n#pragma warning restore xUnit1013 // Public method should be marked as test\n    { }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlStorageFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Transactions;\nusing Hangfire.PostgreSql.Factories;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Npgsql;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlStorageFacts\n  {\n    private readonly PostgreSqlStorageOptions _options;\n\n    public PostgreSqlStorageFacts()\n    {\n      _options = new PostgreSqlStorageOptions { PrepareSchemaIfNecessary = false, EnableTransactionScopeEnlistment = true };\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Ctor_CanCreateSqlServerStorage_WithExistingConnection()\n    {\n      NpgsqlConnection connection = ConnectionUtils.CreateConnection();\n      PostgreSqlStorage storage = new(new ExistingNpgsqlConnectionFactory(connection, _options), _options);\n\n      Assert.NotNull(storage);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Ctor_InitializesDefaultJobQueueProvider_AndPassesCorrectOptions()\n    {\n      PostgreSqlStorage storage = CreateStorage();\n      PersistentJobQueueProviderCollection providers = storage.QueueProviders;\n\n      PostgreSqlJobQueueProvider provider = (PostgreSqlJobQueueProvider)providers.GetProvider(\"default\");\n\n      Assert.Same(_options, provider.Options);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetMonitoringApi_ReturnsNonNullInstance()\n    {\n      PostgreSqlStorage storage = CreateStorage();\n      IMonitoringApi api = storage.GetMonitoringApi();\n      Assert.NotNull(api);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void GetComponents_ReturnsAllNeededComponents()\n    {\n      PostgreSqlStorage storage = CreateStorage();\n\n#pragma warning disable CS0618 // Type or member is obsolete\n      IEnumerable<IServerComponent> components = storage.GetComponents();\n#pragma warning restore CS0618 // Type or member is obsolete\n\n      Type[] componentTypes = components.Select(x => x.GetType()).ToArray();\n      Assert.Contains(typeof(ExpirationManager), componentTypes);\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_WhenConnectionFactoryIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlStorage(connectionFactory: null, new PostgreSqlStorageOptions()));\n      Assert.Equal(\"connectionFactory\", exception.ParamName);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void Ctor_CanCreateSqlServerStorage_WithExistingConnectionFactory()\n    {\n      PostgreSqlStorage storage = new(new DefaultConnectionFactory(), _options);\n      Assert.NotNull(storage);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void CanCreateAndOpenConnection_WithExistingConnectionFactory()\n    {\n      PostgreSqlStorage storage = new(new DefaultConnectionFactory(), _options);\n      NpgsqlConnection connection = storage.CreateAndOpenConnection();\n      Assert.NotNull(connection);\n    }\n\n    [Fact]\n    public void CreateAndOpenConnection_ThrowsAnException_WithExistingConnectionFactoryAndInvalidOptions()\n    {\n      PostgreSqlStorageOptions option = new() {\n        EnableTransactionScopeEnlistment = false,\n        PrepareSchemaIfNecessary = false,\n      };\n      Assert.Throws<ArgumentException>(() => new PostgreSqlStorage(ConnectionUtils.GetDefaultConnectionFactory(option), option));\n    }\n\n    [Fact]\n    public void CanUseTransaction_WithDifferentTransactionIsolationLevel()\n    {\n      using TransactionScope scope = new(TransactionScopeOption.Required,\n        new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable });\n\n      PostgreSqlStorage storage = new(new DefaultConnectionFactory(), _options);\n      NpgsqlConnection connection = storage.CreateAndOpenConnection();\n\n      bool success = storage.UseTransaction(connection, (_, _) => true);\n\n      Assert.True(success);\n    }\n\n    [Fact]\n    public void HasFeature_ThrowsAnException_WhenFeatureIsNull()\n    {\n      ArgumentNullException aex = Assert.Throws<ArgumentNullException>(() => new PostgreSqlStorage(new DefaultConnectionFactory(), _options).HasFeature(null));\n      Assert.Equal(\"featureId\", aex.ParamName);\n    }\n\n    [Theory]\n    [InlineData(\"Job.Queue\", true)] // JobStorageFeatures.JobQueueProperty\n    [InlineData(\"Connection.BatchedGetFirstByLowestScoreFromSet\", true)] // JobStorageFeatures.Connection.BatchedGetFirstByLowest\n    [InlineData(\"\", false)]\n    [InlineData(\"Unsupported\", false)]\n    public void HasFeature_ReturnsCorrectValues(string featureName, bool expected)\n    {\n      PostgreSqlStorage storage = new(new DefaultConnectionFactory(), _options);\n      bool actual = storage.HasFeature(featureName);\n      Assert.Equal(expected, actual);\n    }\n\n    [Fact]\n    public void Ctor_RetriesInitialization_WhenResilientStartupIsEnabled_AndConnectionFails()\n    {\n      // Arrange\n      PostgreSqlStorageOptions options = new()\n      {\n        PrepareSchemaIfNecessary = true,\n        StartupConnectionMaxRetries = 2,\n        StartupConnectionBaseDelay = TimeSpan.FromMilliseconds(1),\n        StartupConnectionMaxDelay = TimeSpan.FromMilliseconds(2),\n        AllowDegradedModeWithoutStorage = false,\n      };\n\n      int callCount = 0;\n      IConnectionFactory failingFactory = new DelegateConnectionFactory(() =>\n      {\n        callCount++;\n        throw new NpgsqlException(\"Simulated connection failure\");\n      });\n\n      // Act & assert\n      InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => new PostgreSqlStorage(failingFactory, options));\n      Assert.Contains(\"Failed to initialize Hangfire PostgreSQL storage.\", ex.Message);\n      Assert.Equal(1 + options.StartupConnectionMaxRetries, callCount);\n    }\n\n    [Fact]\n    public void Ctor_AllowsDegradedMode_WhenResilientStartupIsEnabled_AndDegradedModeAllowed()\n    {\n      // Arrange\n      PostgreSqlStorageOptions options = new()\n      {\n        PrepareSchemaIfNecessary = true,\n        StartupConnectionMaxRetries = 1,\n        StartupConnectionBaseDelay = TimeSpan.FromMilliseconds(1),\n        StartupConnectionMaxDelay = TimeSpan.FromMilliseconds(2),\n        AllowDegradedModeWithoutStorage = true,\n      };\n\n      int callCount = 0;\n      IConnectionFactory failingFactory = new DelegateConnectionFactory(() =>\n      {\n        callCount++;\n        throw new NpgsqlException(\"Simulated connection failure\");\n      });\n\n      // Act: constructor should not throw due to degraded mode\n      PostgreSqlStorage storage = new(failingFactory, options);\n      Assert.NotNull(storage);\n\n      // Lazy initialization should also not throw when degraded mode is enabled,\n      // even if the storage still cannot be initialized.\n      storage.GetConnection();\n\n      // Ensure that initialization was attempted more than once (startup + lazy init)\n      Assert.True(callCount >= 1 + options.StartupConnectionMaxRetries);\n    }\n\n    private PostgreSqlStorage CreateStorage()\n    {\n      return new PostgreSqlStorage(ConnectionUtils.GetDefaultConnectionFactory(), _options);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlStorageOptionsFacts.cs",
    "content": "﻿using System;\nusing Xunit;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlStorageOptionsFacts\n  {\n    [Fact]\n    public void Ctor_SetsTheDefaultOptions()\n    {\n      PostgreSqlStorageOptions options = new();\n\n      Assert.True(options.QueuePollInterval > TimeSpan.Zero);\n      Assert.True(options.InvisibilityTimeout > TimeSpan.Zero);\n      Assert.True(options.DistributedLockTimeout > TimeSpan.Zero);\n      Assert.True(options.PrepareSchemaIfNecessary);\n\n      // Resilient startup defaults\n      Assert.Equal(5, options.StartupConnectionMaxRetries);\n      Assert.True(options.EnableResilientStartup);\n      Assert.Equal(TimeSpan.FromSeconds(1), options.StartupConnectionBaseDelay);\n      Assert.Equal(TimeSpan.FromMinutes(1), options.StartupConnectionMaxDelay);\n      Assert.True(options.AllowDegradedModeWithoutStorage);\n    }\n\n    [Fact]\n    public void EnableResilientStartup_IsFalse_WhenStartupConnectionMaxRetriesIsZero()\n    {\n      PostgreSqlStorageOptions options = new() { StartupConnectionMaxRetries = 0 };\n      Assert.False(options.EnableResilientStartup);\n    }\n\n    [Fact]\n    public void EnableResilientStartup_IsTrue_WhenStartupConnectionMaxRetriesIsPositive()\n    {\n      PostgreSqlStorageOptions options = new() { StartupConnectionMaxRetries = 3 };\n      Assert.True(options.EnableResilientStartup);\n    }\n\n    [Fact]\n    public void Set_QueuePollInterval_ShouldThrowAnException_WhenGivenIntervalIsTooLow()\n    {\n      PostgreSqlStorageOptions options = new();\n      Assert.Throws<ArgumentException>(() => options.QueuePollInterval = TimeSpan.FromMilliseconds(10));\n    }\n\n    [Fact]\n    public void Set_QueuePollInterval_SetsTheValue_WhenGivenIntervalIsTooLow_ButIgnored()\n    {\n      PostgreSqlStorageOptions options = new() {\n        AllowUnsafeValues = true,\n        QueuePollInterval = TimeSpan.FromMilliseconds(10),\n      };\n      Assert.Equal(TimeSpan.FromMilliseconds(10), options.QueuePollInterval);\n    }\n\n    [Fact]\n    public void Set_QueuePollInterval_ShouldThrowAnException_WhenGivenIntervalIsEqualToZero_EvenIfIgnored()\n    {\n      PostgreSqlStorageOptions options = new() { AllowUnsafeValues = true };\n      Assert.Throws<ArgumentException>(() => options.QueuePollInterval = TimeSpan.Zero);\n    }\n\n    [Fact]\n    public void Set_QueuePollInterval_SetsTheValue()\n    {\n      PostgreSqlStorageOptions options = new();\n      options.QueuePollInterval = TimeSpan.FromSeconds(1);\n      Assert.Equal(TimeSpan.FromSeconds(1), options.QueuePollInterval);\n    }\n\n    [Fact]\n    public void Set_InvisibilityTimeout_ShouldThrowAnException_WhenGivenIntervalIsEqualToZero()\n    {\n      PostgreSqlStorageOptions options = new();\n      Assert.Throws<ArgumentException>(() => options.InvisibilityTimeout = TimeSpan.Zero);\n    }\n\n    [Fact]\n    public void Set_InvisibilityTimeout_ShouldThrowAnException_WhenGivenIntervalIsNegative()\n    {\n      PostgreSqlStorageOptions options = new();\n      Assert.Throws<ArgumentException>(() => options.InvisibilityTimeout = TimeSpan.FromSeconds(-1));\n    }\n\n    [Fact]\n    public void Set_InvisibilityTimeout_SetsTheValue()\n    {\n      PostgreSqlStorageOptions options = new();\n      options.InvisibilityTimeout = TimeSpan.FromSeconds(1);\n      Assert.Equal(TimeSpan.FromSeconds(1), options.InvisibilityTimeout);\n    }\n\n    [Fact]\n    public void Set_DistributedLockTimeout_ShouldThrowAnException_WhenGivenIntervalIsEqualToZero()\n    {\n      PostgreSqlStorageOptions options = new();\n      Assert.Throws<ArgumentException>(() => options.DistributedLockTimeout = TimeSpan.Zero);\n    }\n\n    [Fact]\n    public void Set_DistributedLockTimeout_ShouldThrowAnException_WhenGivenIntervalIsNegative()\n    {\n      PostgreSqlStorageOptions options = new();\n      Assert.Throws<ArgumentException>(() => options.DistributedLockTimeout = TimeSpan.FromSeconds(-1));\n    }\n\n    [Fact]\n    public void Set_DistributedLockTimeout_SetsTheValue()\n    {\n      PostgreSqlStorageOptions options = new();\n      options.DistributedLockTimeout = TimeSpan.FromSeconds(1);\n      Assert.Equal(TimeSpan.FromSeconds(1), options.DistributedLockTimeout);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/PostgreSqlWriteOnlyTransactionFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text.Json;\nusing System.Threading.Tasks;\nusing System.Transactions;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.PostgreSql.Factories;\nusing Hangfire.PostgreSql.Tests.Entities;\nusing Hangfire.PostgreSql.Tests.Utils;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Npgsql;\nusing Xunit;\nusing IsolationLevel = System.Transactions.IsolationLevel;\n\nnamespace Hangfire.PostgreSql.Tests\n{\n  public class PostgreSqlWriteOnlyTransactionFacts : IClassFixture<PostgreSqlStorageFixture>\n  {\n    private readonly PostgreSqlStorageFixture _fixture;\n\n    public PostgreSqlWriteOnlyTransactionFacts(PostgreSqlStorageFixture fixture)\n    {\n      _fixture = fixture;\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_IfStorageIsNull()\n    {\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => new PostgreSqlWriteOnlyTransaction(null, () => null));\n\n      Assert.Equal(\"storage\", exception.ParamName);\n    }\n\n    [Fact]\n    public void Ctor_ThrowsAnException_IfDedicatedConnectionFuncIsNull()\n    {\n      PostgreSqlStorageOptions options = new() { EnableTransactionScopeEnlistment = true };\n      ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() =>\n        new PostgreSqlWriteOnlyTransaction(new PostgreSqlStorage(new ExistingNpgsqlConnectionFactory(ConnectionUtils.CreateConnection(), options), options), null));\n\n      Assert.Equal(\"dedicatedConnectionFunc\", exception.ParamName);\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void ExpireJob_SetsJobExpirationData()\n    {\n\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\"(\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', @When) RETURNING \"\"id\"\"\n      \";\n\n      UseConnection(connection => {\n        DateTime utcNow = DateTime.UtcNow;\n        string jobId = connection.QuerySingle<long>(arrangeSql, new { When = utcNow }).ToString(CultureInfo.InvariantCulture);\n        string anotherJobId = connection.QuerySingle<long>(arrangeSql, new { When = utcNow }).ToString(CultureInfo.InvariantCulture);\n\n        Commit(connection, x => x.ExpireJob(jobId, TimeSpan.FromDays(1)));\n\n        TestJob job = Helper.GetTestJob(connection, GetSchemaName(), jobId);\n        Assert.True(utcNow.AddMinutes(-1) < job.ExpireAt && job.ExpireAt <= utcNow.AddDays(1).AddSeconds(5));\n\n        TestJob anotherJob = Helper.GetTestJob(connection, GetSchemaName(), anotherJobId);\n        Assert.Null(anotherJob.ExpireAt);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void PersistJob_ClearsTheJobExpirationData()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\", \"\"expireat\"\")\n        VALUES ('{{}}', '[]', NOW(), NOW()) RETURNING \"\"id\"\"\n      \";\n\n      UseConnection(connection => {\n        string jobId = connection.QuerySingle<long>(arrangeSql).ToString(CultureInfo.InvariantCulture);\n        string anotherJobId = connection.QuerySingle<long>(arrangeSql).ToString(CultureInfo.InvariantCulture);\n\n        Commit(connection, x => x.PersistJob(jobId));\n\n        TestJob job = Helper.GetTestJob(connection, GetSchemaName(), jobId);\n        Assert.Null(job.ExpireAt);\n\n        TestJob anotherJob = Helper.GetTestJob(connection, GetSchemaName(), anotherJobId);\n        Assert.NotNull(anotherJob.ExpireAt);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetJobState_AppendsAStateAndSetItToTheJob()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', NOW()) RETURNING \"\"id\"\"\";\n\n      UseConnection(connection => {\n        dynamic jobId = connection.QuerySingle(arrangeSql).id.ToString();\n        dynamic anotherJobId = connection.QuerySingle(arrangeSql).id.ToString();\n\n        Mock<IState> state = new();\n        state.Setup(x => x.Name).Returns(\"State\");\n        state.Setup(x => x.Reason).Returns(\"Reason\");\n        state.Setup(x => x.SerializeData())\n          .Returns(new Dictionary<string, string> { { \"Name\", \"Value\" } });\n\n        Commit(connection, x => x.SetJobState(jobId, state.Object));\n\n        TestJob job = Helper.GetTestJob(connection, GetSchemaName(), jobId);\n\n        Assert.Equal(\"State\", job.StateName);\n        Assert.NotNull(job.StateId);\n\n        TestJob anotherJob = Helper.GetTestJob(connection, GetSchemaName(), anotherJobId);\n        Assert.Null(anotherJob.StateName);\n        Assert.Null(anotherJob.StateId);\n\n        dynamic jobState = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"state\"\"\");\n        Assert.Equal((string)jobId, jobState.jobid.ToString());\n        Assert.Equal(\"State\", jobState.name);\n        Assert.Equal(\"Reason\", jobState.reason);\n        Assert.NotNull(jobState.createdat);\n\n        Dictionary<string, string> data = JsonSerializer.Deserialize<Dictionary<string, string>>(jobState.data);\n        KeyValuePair<string, string> value = Assert.Single(data);\n        Assert.Equal(\"Name\", value.Key);\n        Assert.Equal(\"Value\", value.Value);\n      });\n    }\n\n    [Theory]\n    [CleanDatabase]\n    [InlineData(false)]\n    [InlineData(true)]\n    public void SetJobState_EnlistsInAmbientTransaction(bool completeTransactionScope)\n    {\n      TransactionScope CreateTransactionScope(IsolationLevel isolationLevel = IsolationLevel.RepeatableRead)\n      {\n        TransactionOptions transactionOptions = new() {\n          IsolationLevel = isolationLevel,\n          Timeout = TransactionManager.MaximumTimeout,\n        };\n\n        return new TransactionScope(TransactionScopeOption.Required, transactionOptions);\n      }\n\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', NOW()) RETURNING \"\"id\"\"\";\n\n\n      string jobId = null;\n      string anotherJobId = null;\n      UseConnection(connection => {\n        jobId = connection.QuerySingle(arrangeSql).id.ToString();\n        anotherJobId = connection.QuerySingle(arrangeSql).id.ToString();\n      });\n\n      using (TransactionScope scope = CreateTransactionScope())\n      {\n        UseConnection(connection => {\n          Mock<IState> state = new();\n          state.Setup(x => x.Name).Returns(\"State\");\n          state.Setup(x => x.Reason).Returns(\"Reason\");\n          state.Setup(x => x.SerializeData())\n            .Returns(new Dictionary<string, string> { { \"Name\", \"Value\" } });\n\n          Commit(connection, x => x.SetJobState(jobId, state.Object));\n        });\n        if (completeTransactionScope)\n        {\n          scope.Complete();\n        }\n      }\n\n      UseConnection(connection => {\n        TestJob job = Helper.GetTestJob(connection, GetSchemaName(), jobId);\n        if (completeTransactionScope)\n        {\n          Assert.Equal(\"State\", job.StateName);\n          Assert.NotNull(job.StateId);\n\n          dynamic jobState = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"state\"\"\");\n          Assert.Equal(jobId, jobState.jobid.ToString());\n          Assert.Equal(\"State\", jobState.name);\n          Assert.Equal(\"Reason\", jobState.reason);\n          Assert.NotNull(jobState.createdat);\n\n          Dictionary<string, string> data = JsonSerializer.Deserialize<Dictionary<string, string>>(jobState.data);\n          KeyValuePair<string, string> value = Assert.Single(data);\n          Assert.Equal(\"Name\", value.Key);\n          Assert.Equal(\"Value\", value.Value);\n        }\n        else\n        {\n          Assert.Null(job.StateName);\n          Assert.Null(job.StateId);\n\n          Assert.Null(connection.QuerySingleOrDefault($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"state\"\"\"));\n        }\n\n        TestJob anotherJob = Helper.GetTestJob(connection, GetSchemaName(), anotherJobId);\n        Assert.Null(anotherJob.StateName);\n        Assert.Null(anotherJob.StateId);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddJobState_JustAddsANewRecordInATable()\n    {\n      string arrangeSql = $@\"\n        INSERT INTO \"\"{GetSchemaName()}\"\".\"\"job\"\" (\"\"invocationdata\"\", \"\"arguments\"\", \"\"createdat\"\")\n        VALUES ('{{}}', '[]', NOW())\n        RETURNING \"\"id\"\"\n      \";\n\n      Dictionary<string, string> expectedData = new() { { \"Name\", \"Value\" } };\n\n      UseConnection(connection => {\n        dynamic jobId = connection.QuerySingle(arrangeSql).id.ToString(CultureInfo.InvariantCulture);\n\n        Mock<IState> state = new();\n        state.Setup(x => x.Name).Returns(\"State\");\n        state.Setup(x => x.Reason).Returns(\"Reason\");\n        state.Setup(x => x.SerializeData()).Returns(expectedData);\n\n        Commit(connection, x => x.AddJobState(jobId, state.Object));\n\n        TestJob job = Helper.GetTestJob(connection, GetSchemaName(), jobId);\n        Assert.Null(job.StateName);\n        Assert.Null(job.StateId);\n\n        dynamic jobState = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"state\"\"\");\n        Assert.Equal((string)jobId, jobState.jobid.ToString(CultureInfo.InvariantCulture));\n        Assert.Equal(\"State\", jobState.name);\n        Assert.Equal(\"Reason\", jobState.reason);\n        Assert.NotNull(jobState.createdat);\n\n        Dictionary<string, string> data = JsonSerializer.Deserialize<Dictionary<string, string>>(jobState.data);\n        KeyValuePair<string, string> value = Assert.Single(data);\n        Assert.Equal(\"Name\", value.Key);\n        Assert.Equal(\"Value\", value.Value);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddToQueue_CallsEnqueue_OnTargetPersistentQueue()\n    {\n      UseConnection(connection => {\n        Mock<IPersistentJobQueue> correctJobQueue = new();\n        Mock<IPersistentJobQueueProvider> correctProvider = new();\n        correctProvider.Setup(x => x.GetJobQueue())\n          .Returns(correctJobQueue.Object);\n\n        _fixture.PersistentJobQueueProviderCollection.Add(correctProvider.Object, new[] { \"default\" });\n\n        try\n        {\n          Commit(connection, x => x.AddToQueue(\"default\", \"1\"));\n\n          correctJobQueue.Verify(x => x.Enqueue(connection, \"default\", \"1\"));\n        }\n        finally\n        {\n          _fixture.PersistentJobQueueProviderCollection.Remove(\"default\");\n        }\n      });\n    }\n\n\n    [Fact]\n    [CleanDatabase]\n    public void IncrementCounter_AddsRecordToCounterTable_WithPositiveValue()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.IncrementCounter(\"my-key\"));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\");\n\n        Assert.Equal(\"my-key\", record.key);\n        Assert.Equal(1, record.value);\n        Assert.Equal((DateTime?)null, record.expireat);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void IncrementCounter_WithExpiry_AddsARecord_WithExpirationTimeSet()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.IncrementCounter(\"my-key\", TimeSpan.FromDays(1)));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\");\n\n        Assert.Equal(\"my-key\", record.key);\n        Assert.Equal(1, record.value);\n        Assert.NotNull(record.expireat);\n\n        DateTime expireAt = (DateTime)record.expireat;\n\n        Assert.True(DateTime.UtcNow.AddHours(23) < expireAt);\n        Assert.True(expireAt < DateTime.UtcNow.AddHours(25));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void IncrementCounter_WithExistingKey_AddsAnotherRecord()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.IncrementCounter(\"my-key\");\n          x.IncrementCounter(\"my-key\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\");\n\n        Assert.Equal(2, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void DecrementCounter_AddsRecordToCounterTable_WithNegativeValue()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.DecrementCounter(\"my-key\"));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\");\n\n        Assert.Equal(\"my-key\", record.key);\n        Assert.Equal(-1, record.value);\n        Assert.Equal((DateTime?)null, record.expireat);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void DecrementCounter_WithExpiry_AddsARecord_WithExpirationTimeSet()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.DecrementCounter(\"my-key\", TimeSpan.FromDays(1)));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\");\n\n        Assert.Equal(\"my-key\", record.key);\n        Assert.Equal(-1, record.value);\n        Assert.NotNull(record.expireat);\n\n        DateTime expireAt = (DateTime)record.expireat;\n\n        Assert.True(DateTime.UtcNow.AddHours(23) < expireAt);\n        Assert.True(expireAt < DateTime.UtcNow.AddHours(25));\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void DecrementCounter_WithExistingKey_AddsAnotherRecord()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.DecrementCounter(\"my-key\");\n          x.DecrementCounter(\"my-key\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"counter\"\"\");\n\n        Assert.Equal(2, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddToSet_AddsARecord_IfThereIsNo_SuchKeyAndValue()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.AddToSet(\"my-key\", \"my-value\"));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(\"my-key\", record.key);\n        Assert.Equal(\"my-value\", record.value);\n        Assert.Equal(0.0, record.score, 2);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddToSet_AddsARecord_WhenKeyIsExists_ButValuesAreDifferent()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.AddToSet(\"my-key\", \"my-value\");\n          x.AddToSet(\"my-key\", \"another-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(2, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddToSet_DoesNotAddARecord_WhenBothKeyAndValueAreExist()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.AddToSet(\"my-key\", \"my-value\");\n          x.AddToSet(\"my-key\", \"my-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(1, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddToSet_WithScore_AddsARecordWithScore_WhenBothKeyAndValueAreNotExist()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.AddToSet(\"my-key\", \"my-value\", 3.2));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(\"my-key\", record.key);\n        Assert.Equal(\"my-value\", record.value);\n        Assert.Equal(3.2, record.score, 3);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddToSet_WithScore_UpdatesAScore_WhenBothKeyAndValueAreExist()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.AddToSet(\"my-key\", \"my-value\");\n          x.AddToSet(\"my-key\", \"my-value\", 3.2);\n        });\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(3.2, record.score, 3);\n      });\n    }\n\n    [SkippableFact]\n    [CleanDatabase]\n    public void AddToSet_DoesNotFailWithConcurrencyError_WhenRunningMultipleThreads()\n    {\n      if (Environment.ProcessorCount < 2)\n      {\n        throw new SkipException(\"You need to have more than 1 CPU to run the test\");\n      }\n\n      void CommitTags(PostgreSqlWriteOnlyTransaction transaction, IEnumerable<string> tags, string jobId)\n      {\n        //Imitating concurrency issue scenario from Hangfire.Tags library.\n        //Details: https://github.com/frankhommers/Hangfire.PostgreSql/issues/191\n\n        foreach (string tag in tags)\n        {\n          long score = DateTime.Now.Ticks;\n\n          transaction.AddToSet(\"tags\", tag, score);\n          transaction.AddToSet($\"tags:{jobId}\", tag, score);\n          transaction.AddToSet($\"tags:{tag}\", jobId, score);\n        }\n      }\n\n      const int loopIterations = 1_000;\n      const int jobGroups = 10;\n      const int totalTagsCount = 2;\n\n      Parallel.For(1, 1 + loopIterations, i => {\n        UseDisposableConnection(sql => {\n          CommitDisposable(sql, x => {\n            int jobTypeIndex = i % jobGroups;\n            CommitTags(x, new[] { \"my-shared-tag\", $\"job-type-{jobTypeIndex}\" }, i.ToString(CultureInfo.InvariantCulture));\n          });\n        });\n      });\n\n      UseConnection(connection => {\n        int jobsCountUnderMySharedTag = connection.QuerySingle<int>($@\"\n          SELECT COUNT(*) \n          FROM \"\"{GetSchemaName()}\"\".set\n          WHERE key LIKE 'tags:my-shared-tag'\");\n        Assert.Equal(loopIterations, jobsCountUnderMySharedTag);\n\n\n        int[] jobsCountsUnderJobTypeTags = connection.Query<int>($@\"\n          SELECT COUNT(*)\n          FROM \"\"{GetSchemaName()}\"\".set\n          where key like 'tags:job-type-%'\n          group by key;\").ToArray();\n\n        Assert.All(jobsCountsUnderJobTypeTags, count => Assert.Equal(loopIterations / jobGroups, count));\n\n        int jobLinkTagsCount = connection.QuerySingle<int>($@\"\n          SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".set\n          where value ~ '^\\d+$'\n        \");\n\n        Assert.Equal(loopIterations * totalTagsCount, jobLinkTagsCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromSet_RemovesARecord_WithGivenKeyAndValue()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.AddToSet(\"my-key\", \"my-value\");\n          x.RemoveFromSet(\"my-key\", \"my-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(0, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromSet_DoesNotRemoveRecord_WithSameKey_AndDifferentValue()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.AddToSet(\"my-key\", \"my-value\");\n          x.RemoveFromSet(\"my-key\", \"different-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(1, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromSet_DoesNotRemoveRecord_WithSameValue_AndDifferentKey()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.AddToSet(\"my-key\", \"my-value\");\n          x.RemoveFromSet(\"different-key\", \"my-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n\n        Assert.Equal(1, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void InsertToList_AddsARecord_WithGivenValues()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.InsertToList(\"my-key\", \"my-value\"));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(\"my-key\", record.key);\n        Assert.Equal(\"my-value\", record.value);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void InsertToList_AddsAnotherRecord_WhenBothKeyAndValueAreExist()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"my-value\");\n          x.InsertToList(\"my-key\", \"my-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n        Assert.Equal(2, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromList_RemovesAllRecords_WithGivenKeyAndValue()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"my-value\");\n          x.InsertToList(\"my-key\", \"my-value\");\n          x.RemoveFromList(\"my-key\", \"my-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(0, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromList_DoesNotRemoveRecords_WithSameKey_ButDifferentValue()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"my-value\");\n          x.RemoveFromList(\"my-key\", \"different-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(1, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveFromList_DoesNotRemoveRecords_WithSameValue_ButDifferentKey()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"my-value\");\n          x.RemoveFromList(\"different-key\", \"my-value\");\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(1, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void TrimList_TrimsAList_ToASpecifiedRange()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"0\");\n          x.InsertToList(\"my-key\", \"1\");\n          x.InsertToList(\"my-key\", \"2\");\n          x.InsertToList(\"my-key\", \"3\");\n          x.TrimList(\"my-key\", 1, 2);\n        });\n\n        dynamic[] records = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\").ToArray();\n\n        Assert.Equal(2, records.Length);\n        Assert.Equal(\"1\", records[0].value);\n        Assert.Equal(\"2\", records[1].value);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void TrimList_RemovesRecordsToEnd_IfKeepAndingAt_GreaterThanMaxElementIndex()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"0\");\n          x.InsertToList(\"my-key\", \"1\");\n          x.InsertToList(\"my-key\", \"2\");\n          x.TrimList(\"my-key\", 1, 100);\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(2, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void TrimList_RemovesAllRecords_WhenStartingFromValue_GreaterThanMaxElementIndex()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"0\");\n          x.TrimList(\"my-key\", 1, 100);\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(0, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void TrimList_RemovesAllRecords_IfStartFromGreaterThanEndingAt()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"0\");\n          x.TrimList(\"my-key\", 1, 0);\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(0, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void TrimList_RemovesRecords_OnlyOfAGivenKey()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => {\n          x.InsertToList(\"my-key\", \"0\");\n          x.TrimList(\"another-key\", 1, 0);\n        });\n\n        long recordCount = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\");\n\n        Assert.Equal(1, recordCount);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetRangeInHash_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(\n          () => Commit(connection, x => x.SetRangeInHash(null, new Dictionary<string, string>())));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetRangeInHash_ThrowsAnException_WhenKeyValuePairsArgumentIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.SetRangeInHash(\"some-hash\", null)));\n\n        Assert.Equal(\"keyValuePairs\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void SetRangeInHash_MergesAllRecords()\n    {\n      UseConnection(connection => {\n        Commit(connection, x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string> {\n          { \"Key1\", \"Value1\" },\n          { \"Key2\", \"Value2\" },\n        }));\n\n        Dictionary<string, string> result = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"hash\"\" WHERE \"\"key\"\" = @Key\",\n            new { Key = \"some-hash\" })\n          .ToDictionary(x => (string)x.field, x => (string)x.value);\n\n        Assert.Equal(\"Value1\", result[\"Key1\"]);\n        Assert.Equal(\"Value2\", result[\"Key2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveHash_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.RemoveHash(null))); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveHash_RemovesAllHashRecords()\n    {\n      UseConnection(connection => {\n        // Arrange\n        Commit(connection, x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string> {\n          { \"Key1\", \"Value1\" },\n          { \"Key2\", \"Value2\" },\n        }));\n\n        // Act\n        Commit(connection, x => x.RemoveHash(\"some-hash\"));\n\n        // Assert\n        long count = connection.QuerySingle<long>($@\"SELECT COUNT(*) FROM \"\"{GetSchemaName()}\"\".\"\"hash\"\"\");\n        Assert.Equal(0, count);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddRangeToSet_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.AddRangeToSet(null, new List<string>())));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddRangeToSet_ThrowsAnException_WhenItemsValueIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.AddRangeToSet(\"my-set\", null)));\n\n        Assert.Equal(\"items\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddRangeToSet_AddsAllItems_ToAGivenSet()\n    {\n      UseConnection(connection => {\n        List<string> items = new() { \"1\", \"2\", \"3\" };\n\n        Commit(connection, x => x.AddRangeToSet(\"my-set\", items));\n\n        IEnumerable<string> records = connection.Query<string>($@\"SELECT \"\"value\"\" FROM \"\"{GetSchemaName()}\"\".\"\"set\"\" WHERE \"\"key\"\" = 'my-set'\");\n        Assert.Equal(items, records);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveSet_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => { Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.RemoveSet(null))); });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void RemoveSet_RemovesASet_WithAGivenKey()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"value\"\", \"\"score\"\") VALUES (@Key, @Value, 0.0)\";\n\n      UseConnection(connection => {\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"set-1\", Value = \"1\" },\n          new { Key = \"set-2\", Value = \"1\" },\n        });\n\n        Commit(connection, x => x.RemoveSet(\"set-1\"));\n\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\");\n        Assert.Equal(\"set-2\", record.key);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void ExpireHash_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => \n          Commit(connection, x => x.ExpireHash(null, TimeSpan.FromMinutes(5)))\n            );\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void ExpireHash_SetsExpirationTimeOnAHash_WithGivenKey()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".hash (\"\"key\"\", \"\"field\"\") VALUES (@Key, @Field)\";\n\n      UseConnection(connection => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"hash-1\", Field = \"field\" },\n          new { Key = \"hash-2\", Field = \"field\" },\n        });\n\n        // Act\n        Commit(connection, x => x.ExpireHash(\"hash-1\", TimeSpan.FromMinutes(60)));\n\n        // Assert\n        Dictionary<string, DateTime?> records = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".hash\")\n          .ToDictionary(x => (string)x.key, x => (DateTime?)x.expireat);\n        Assert.True(DateTime.UtcNow.AddMinutes(59) < records[\"hash-1\"]);\n        Assert.True(records[\"hash-1\"] < DateTime.UtcNow.AddMinutes(61));\n        Assert.Null(records[\"hash-2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void ExpireSet_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => \n          Commit(connection, x => x.ExpireSet(null, TimeSpan.FromSeconds(45)))\n            );\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void ExpireSet_SetsExpirationTime_OnASet_WithGivenKey()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"value\"\", \"\"score\"\") VALUES (@Key, @Value, 0.0)\";\n\n      UseConnection(connection => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"set-1\", Value = \"1\" },\n          new { Key = \"set-2\", Value = \"1\" },\n        });\n\n        // Act\n        Commit(connection, x => x.ExpireSet(\"set-1\", TimeSpan.FromMinutes(60)));\n\n        // Assert\n        Dictionary<string, DateTime?> records = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\")\n          .ToDictionary(x => (string)x.key, x => (DateTime?)x.expireat);\n        Assert.True(DateTime.UtcNow.AddMinutes(59) < records[\"set-1\"]);\n        Assert.True(records[\"set-1\"] < DateTime.UtcNow.AddMinutes(61));\n        Assert.Null(records[\"set-2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void ExpireList_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => \n          Commit(connection, x => x.ExpireList(null, TimeSpan.FromSeconds(45)))\n            );\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void ExpireList_SetsExpirationTime_OnAList_WithGivenKey()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"list\"\" (\"\"key\"\") VALUES (@Key)\";\n\n      UseConnection(connection => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"list-1\" },\n          new { Key = \"list-2\" },\n        });\n\n        // Act\n        Commit(connection, x => x.ExpireList(\"list-1\", TimeSpan.FromMinutes(60)));\n\n        // Assert\n        Dictionary<string, DateTime?> records = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\")\n          .ToDictionary(x => (string)x.key, x => (DateTime?)x.expireat);\n        Assert.True(DateTime.UtcNow.AddMinutes(59) < records[\"list-1\"]);\n        Assert.True(records[\"list-1\"] < DateTime.UtcNow.AddMinutes(61));\n        Assert.Null(records[\"list-2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void PersistHash_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.PersistHash(null)));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void PersistHash_ClearsExpirationTime_OnAGivenHash()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".hash (\"\"key\"\", \"\"field\"\", \"\"expireat\"\") VALUES (@Key, @Field, @ExpireAt)\";\n\n      UseConnection(connection => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"hash-1\", Field = \"field\", ExpireAt = DateTime.UtcNow.AddDays(1) },\n          new { Key = \"hash-2\", Field = \"field\", ExpireAt = DateTime.UtcNow.AddDays(1) },\n        });\n\n        // Act\n        Commit(connection, x => x.PersistHash(\"hash-1\"));\n\n        // Assert\n        Dictionary<string, DateTime?> records = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".hash\")\n          .ToDictionary(x => (string)x.key, x => (DateTime?)x.expireat);\n        Assert.Null(records[\"hash-1\"]);\n        Assert.NotNull(records[\"hash-2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void PersistSet_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.PersistSet(null)));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void PersistSet_ClearsExpirationTime_OnAGivenHash()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"set\"\" (\"\"key\"\", \"\"value\"\", \"\"expireat\"\", \"\"score\"\") VALUES (@Key, @Value, @ExpireAt, 0.0)\";\n\n      UseConnection(connection => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"set-1\", Value = \"1\", ExpireAt = DateTime.UtcNow.AddDays(1) },\n          new { Key = \"set-2\", Value = \"1\", ExpireAt = DateTime.UtcNow.AddDays(1) },\n        });\n\n        // Act\n        Commit(connection, x => x.PersistSet(\"set-1\"));\n\n        // Assert\n        Dictionary<string, DateTime?> records = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"set\"\"\")\n          .ToDictionary(x => (string)x.key, x => (DateTime?)x.expireat);\n        Assert.Null(records[\"set-1\"]);\n        Assert.NotNull(records[\"set-2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void PersistList_ThrowsAnException_WhenKeyIsNull()\n    {\n      UseConnection(connection => {\n        ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => Commit(connection, x => x.PersistList(null)));\n\n        Assert.Equal(\"key\", exception.ParamName);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void PersistList_ClearsExpirationTime_OnAGivenHash()\n    {\n      string arrangeSql = $@\"INSERT INTO \"\"{GetSchemaName()}\"\".\"\"list\"\" (\"\"key\"\", \"\"expireat\"\") VALUES (@Key, @ExpireAt)\";\n\n      UseConnection(connection => {\n        // Arrange\n        connection.Execute(arrangeSql, new[] {\n          new { Key = \"list-1\", ExpireAt = DateTime.UtcNow.AddDays(1) },\n          new { Key = \"list-2\", ExpireAt = DateTime.UtcNow.AddDays(1) },\n        });\n\n        // Act\n        Commit(connection, x => x.PersistList(\"list-1\"));\n\n        // Assert\n        Dictionary<string, DateTime?> records = connection.Query($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"list\"\"\")\n          .ToDictionary(x => (string)x.key, x => (DateTime?)x.expireat);\n        Assert.Null(records[\"list-1\"]);\n        Assert.NotNull(records[\"list-2\"]);\n      });\n    }\n\n    [Fact]\n    [CleanDatabase]\n    public void AddToQueue_AddsAJobToTheQueue_UsingStorageConnection_WithTransactionScopeEnlistment()\n    {\n      string jobId;\n\n      using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Required,\n               new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted },\n               TransactionScopeAsyncFlowOption.Enabled))\n      {\n        // Need to run a query within that transaction. If PostgreSqlStorage modifies the connection string, TransactionAbortedExceptions appear\n        // because of prepared transactions.\n        string connectionString = ConnectionUtils.GetConnectionString();\n        using (NpgsqlConnection connection = new NpgsqlConnection(connectionString))\n        {\n          dynamic _ = connection.QueryFirstOrDefault($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\");\n        }\n\n        PostgreSqlStorageOptions options = new() { EnableTransactionScopeEnlistment = true };\n        PostgreSqlStorage storage = new(new NpgsqlConnectionFactory(connectionString, options), options);\n        using (IStorageConnection storageConnection = storage.GetConnection())\n        {\n          using (IWriteOnlyTransaction writeTransaction = storageConnection.CreateWriteTransaction())\n          {\n            // Explicitly call multiple write commands here, as AddToQueue previously opened an own connection.\n            // This triggered a prepared transaction which should be avoided.\n            jobId = storageConnection.CreateExpiredJob(Job.FromExpression(() => Console.Write(\"Hi\")), new Dictionary<string, string>(), DateTime.UtcNow,\n              TimeSpan.FromMinutes(1));\n\n            writeTransaction.SetJobState(jobId, new ScheduledState(DateTime.UtcNow));\n            writeTransaction.AddToQueue(\"default\", jobId);\n            writeTransaction.PersistJob(jobId);\n            writeTransaction.Commit();\n          }\n        }\n\n        transactionScope.Complete();\n      }\n\n      UseConnection(connection => {\n        dynamic record = connection.QuerySingle($@\"SELECT * FROM \"\"{GetSchemaName()}\"\".\"\"jobqueue\"\"\");\n        Assert.Equal(jobId, record.jobid.ToString());\n        Assert.Equal(\"default\", record.queue);\n        Assert.Null(record.FetchedAt);\n      });\n    }\n\n    private void UseConnection(Action<NpgsqlConnection> action)\n    {\n      PostgreSqlStorage storage = _fixture.SafeInit();\n      action(storage.CreateAndOpenConnection());\n    }\n\n    private static void UseDisposableConnection(Action<NpgsqlConnection> action)\n    {\n      using (NpgsqlConnection sqlConnection = ConnectionUtils.CreateConnection())\n      {\n        action(sqlConnection);\n      }\n    }\n\n    private void Commit(NpgsqlConnection connection, Action<PostgreSqlWriteOnlyTransaction> action)\n    {\n      PostgreSqlStorage storage = _fixture.ForceInit(connection);\n      using (IWriteOnlyTransaction transaction = storage.GetConnection().CreateWriteTransaction())\n      {\n        action(transaction as PostgreSqlWriteOnlyTransaction);\n        transaction.Commit();\n      }\n    }\n\n    private void CommitDisposable(NpgsqlConnection connection, Action<PostgreSqlWriteOnlyTransaction> action)\n    {\n      PostgreSqlStorageOptions options = new() {\n        EnableTransactionScopeEnlistment = true,\n        SchemaName = GetSchemaName(),\n      };\n      PostgreSqlStorage storage = new(new ExistingNpgsqlConnectionFactory(connection, options), options);\n      using (IWriteOnlyTransaction transaction = storage.GetConnection().CreateWriteTransaction())\n      {\n        action(transaction as PostgreSqlWriteOnlyTransaction);\n        transaction.Commit();\n      }\n    }\n\n    private static string GetSchemaName()\n    {\n      return ConnectionUtils.GetSchemaName();\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Scripts/Clean.sql",
    "content": "﻿SET search_path = 'hangfire';\n\nDELETE FROM hangfire.\"aggregatedcounter\";\nDELETE FROM hangfire.\"counter\";\nDELETE FROM hangfire.\"hash\";\nDELETE FROM hangfire.\"job\";\nDELETE FROM hangfire.\"jobparameter\";\nDELETE FROM hangfire.\"jobqueue\";\nDELETE FROM hangfire.\"list\";\nDELETE FROM hangfire.\"lock\";\nDELETE FROM hangfire.\"server\";\nDELETE FROM hangfire.\"set\";\nDELETE FROM hangfire.\"state\";"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/CleanDatabaseAttribute.cs",
    "content": "﻿using System.Data;\nusing System.Reflection;\nusing System.Threading;\nusing Dapper;\nusing Npgsql;\nusing Xunit.Sdk;\n\nnamespace Hangfire.PostgreSql.Tests.Utils\n{\n  public class CleanDatabaseAttribute : BeforeAfterTestAttribute\n  {\n    private static readonly object _globalLock = new();\n    private static bool _sqlObjectInstalled;\n\n    public override void Before(MethodInfo methodUnderTest)\n    {\n      Monitor.Enter(_globalLock);\n\n      if (!_sqlObjectInstalled)\n      {\n        RecreateSchemaAndInstallObjects();\n        _sqlObjectInstalled = true;\n      }\n\n      CleanTables();\n    }\n\n    public override void After(MethodInfo methodUnderTest)\n    {\n      try { }\n      finally\n      {\n        Monitor.Exit(_globalLock);\n      }\n    }\n\n    private static void RecreateSchemaAndInstallObjects()\n    {\n      using NpgsqlConnection masterConnection = ConnectionUtils.CreateMasterConnection();\n      bool databaseExists = masterConnection.QuerySingleOrDefault<bool?>($@\"SELECT true :: boolean FROM pg_database WHERE datname = @DatabaseName;\",\n        new {\n          DatabaseName = ConnectionUtils.GetDatabaseName(),\n        }) ?? false;\n\n      if (!databaseExists)\n      {\n        masterConnection.Execute($@\"CREATE DATABASE \"\"{ConnectionUtils.GetDatabaseName()}\"\"\");\n      }\n\n      using NpgsqlConnection connection = ConnectionUtils.CreateConnection();\n      if (connection.State == ConnectionState.Closed)\n      {\n        connection.Open();\n      }\n\n      PostgreSqlObjectsInstaller.Install(connection);\n      PostgreSqlTestObjectsInitializer.CleanTables(connection);\n    }\n\n    private static void CleanTables()\n    {\n      using NpgsqlConnection connection = ConnectionUtils.CreateConnection();\n      PostgreSqlTestObjectsInitializer.CleanTables(connection);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/ConnectionUtils.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing Hangfire.Annotations;\nusing Hangfire.PostgreSql.Factories;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Tests.Utils\n{\n  public static class ConnectionUtils\n  {\n    private const string DatabaseVariable = \"Hangfire_PostgreSql_DatabaseName\";\n    private const string SchemaVariable = \"Hangfire_PostgreSql_SchemaName\";\n\n    private const string ConnectionStringTemplateVariable = \"Hangfire_PostgreSql_ConnectionStringTemplate\";\n\n    private const string MasterDatabaseName = \"postgres\";\n    private const string DefaultDatabaseName = @\"hangfire_tests\";\n    private const string DefaultSchemaName = @\"hangfire\";\n\n    private const string DefaultConnectionStringTemplate = @\"Server=127.0.0.1;Port=5432;Database=postgres;User Id=postgres;Password=password;Include Error Detail=true\";\n\n    public static string GetDatabaseName()\n    {\n      return Environment.GetEnvironmentVariable(DatabaseVariable) ?? DefaultDatabaseName;\n    }\n\n    public static string GetSchemaName()\n    {\n      return Environment.GetEnvironmentVariable(SchemaVariable) ?? DefaultSchemaName;\n    }\n\n    public static string GetMasterConnectionString()\n    {\n      return string.Format(CultureInfo.InvariantCulture, GetConnectionStringTemplate(), MasterDatabaseName);\n    }\n\n    public static string GetConnectionString()\n    {\n      return string.Format(CultureInfo.InvariantCulture, GetConnectionStringTemplate(), GetDatabaseName());\n    }\n\n    public static NpgsqlConnectionFactory GetDefaultConnectionFactory([CanBeNull] PostgreSqlStorageOptions options = null)\n    {\n      return new NpgsqlConnectionFactory(GetConnectionString(), options ?? new PostgreSqlStorageOptions());\n    }\n\n    private static string GetConnectionStringTemplate()\n    {\n      return Environment.GetEnvironmentVariable(ConnectionStringTemplateVariable)\n        ?? DefaultConnectionStringTemplate;\n    }\n\n    public static NpgsqlConnection CreateConnection()\n    {\n      NpgsqlConnectionStringBuilder csb = new(GetConnectionString());\n\n      NpgsqlConnection connection = new() {\n        ConnectionString = csb.ToString(),\n      };\n      connection.Open();\n\n      return connection;\n    }\n\n    public static NpgsqlConnection CreateMasterConnection()\n    {\n      NpgsqlConnectionStringBuilder csb = new(GetMasterConnectionString());\n\n      NpgsqlConnection connection = new() {\n        ConnectionString = csb.ToString(),\n      };\n      connection.Open();\n\n      return connection;\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/DefaultConnectionFactory.cs",
    "content": "﻿using Npgsql;\n\nnamespace Hangfire.PostgreSql.Tests.Utils\n{\n  public class DefaultConnectionFactory : IConnectionFactory\n  {\n    /// <summary>\n    /// Get or create NpgsqlConnection\n    /// </summary>\n    public NpgsqlConnection GetOrCreateConnection()\n    {\n      return ConnectionUtils.CreateConnection();\n    }\n  }\n}"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/DelegateConnectionFactory.cs",
    "content": "﻿using System;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Tests.Utils\n{\n  /// <summary>\n  /// Simple test-only connection factory that delegates connection creation to the provided function.\n  /// </summary>\n  internal sealed class DelegateConnectionFactory : IConnectionFactory\n  {\n    private readonly Func<NpgsqlConnection> _factory;\n\n    public DelegateConnectionFactory(Func<NpgsqlConnection> factory)\n    {\n      _factory = factory ?? throw new ArgumentNullException(nameof(factory));\n    }\n\n    public NpgsqlConnection GetOrCreateConnection()\n    {\n      return _factory();\n    }\n  }\n}\n\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/Helper.cs",
    "content": "using System;\nusing System.Data;\nusing System.Globalization;\nusing Dapper;\nusing Hangfire.PostgreSql.Tests.Entities;\n\nnamespace Hangfire.PostgreSql.Tests.Utils\n{\n  public static class Helper\n  {\n    public static TestJob GetTestJob(IDbConnection connection, string schemaName, string jobId)\n    {\n      return connection\n        .QuerySingle<TestJob>($@\"SELECT \"\"id\"\" \"\"Id\"\", \"\"invocationdata\"\" \"\"InvocationData\"\", \"\"arguments\"\" \"\"Arguments\"\", \"\"expireat\"\" \"\"ExpireAt\"\", \"\"statename\"\" \"\"StateName\"\", \"\"stateid\"\" \"\"StateId\"\", \"\"createdat\"\" \"\"CreatedAt\"\" FROM \"\"{schemaName}\"\".\"\"job\"\" WHERE \"\"id\"\" = @Id OR @Id = -1\",\n          new { Id = Convert.ToInt64(jobId, CultureInfo.InvariantCulture) });\n    }\n\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/PostgreSqlStorageExtensions.cs",
    "content": "﻿namespace Hangfire.PostgreSql.Tests.Utils\n{\n  internal static class PostgreSqlStorageExtensions\n  {\n    public static PostgreSqlConnection GetStorageConnection(this PostgreSqlStorage storage)\n    {\n      return storage.GetConnection() as PostgreSqlConnection;\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/PostgreSqlStorageFixture.cs",
    "content": "﻿using System;\nusing Hangfire.PostgreSql.Factories;\nusing Moq;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Tests.Utils\n{\n  public class PostgreSqlStorageFixture : IDisposable\n  {\n    private readonly PostgreSqlStorageOptions _storageOptions;\n    private bool _initialized;\n    private NpgsqlConnection _mainConnection;\n\n    public PostgreSqlStorageFixture()\n    {\n      PersistentJobQueueMock = new Mock<IPersistentJobQueue>();\n\n      Mock<IPersistentJobQueueProvider> provider = new();\n      provider.Setup(x => x.GetJobQueue())\n        .Returns(PersistentJobQueueMock.Object);\n\n      PersistentJobQueueProviderCollection = new PersistentJobQueueProviderCollection(provider.Object);\n\n      _storageOptions = new PostgreSqlStorageOptions {\n        SchemaName = ConnectionUtils.GetSchemaName(),\n        EnableTransactionScopeEnlistment = true,\n      };\n    }\n\n    public Mock<IPersistentJobQueue> PersistentJobQueueMock { get; }\n\n    public PersistentJobQueueProviderCollection PersistentJobQueueProviderCollection { get; }\n\n    public PostgreSqlStorage Storage { get; private set; }\n    public NpgsqlConnection MainConnection => _mainConnection ?? (_mainConnection = ConnectionUtils.CreateConnection());\n\n    public void Dispose()\n    {\n      _mainConnection?.Dispose();\n      _mainConnection = null;\n    }\n\n    public void SetupOptions(Action<PostgreSqlStorageOptions> storageOptionsConfigure)\n    {\n      storageOptionsConfigure(_storageOptions);\n    }\n\n    public PostgreSqlStorage SafeInit(NpgsqlConnection connection = null)\n    {\n      return _initialized\n        ? Storage\n        : ForceInit(connection);\n    }\n\n    public PostgreSqlStorage ForceInit(NpgsqlConnection connection = null)\n    {\n      Storage = new PostgreSqlStorage(new ExistingNpgsqlConnectionFactory(connection ?? MainConnection, _storageOptions), _storageOptions) {\n        QueueProviders = PersistentJobQueueProviderCollection,\n      };\n      _initialized = true;\n      return Storage;\n    }\n\n    public void SafeInit(PostgreSqlStorageOptions options,\n      PersistentJobQueueProviderCollection jobQueueProviderCollection = null,\n      NpgsqlConnection connection = null)\n    {\n      if (!_initialized)\n      {\n        ForceInit(options, jobQueueProviderCollection, connection);\n        return;\n      }\n\n      Storage.QueueProviders = jobQueueProviderCollection;\n    }\n\n    public void ForceInit(PostgreSqlStorageOptions options,\n      PersistentJobQueueProviderCollection jobQueueProviderCollection = null,\n      NpgsqlConnection connection = null)\n    {\n      Storage = new PostgreSqlStorage(new ExistingNpgsqlConnectionFactory(connection ?? MainConnection, options), options) {\n        QueueProviders = jobQueueProviderCollection,\n      };\n      _initialized = true;\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Hangfire.PostgreSql.Tests/Utils/PostgreSqlTestObjectsInitializer.cs",
    "content": "﻿// This file is part of Hangfire.PostgreSql.\n// Copyright © 2014 Frank Hommers <http://hmm.rs/Hangfire.PostgreSql>.\n// \n// Hangfire.PostgreSql is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire.PostgreSql  is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire.PostgreSql. If not, see <http://www.gnu.org/licenses/>.\n//\n// This work is based on the work of Sergey Odinokov, author of \n// Hangfire. <http://hangfire.io/>\n//   \n//    Special thanks goes to him.\n\nusing System;\nusing System.Data;\nusing System.IO;\nusing System.Reflection;\nusing Npgsql;\n\nnamespace Hangfire.PostgreSql.Tests.Utils\n{\n  internal static class PostgreSqlTestObjectsInitializer\n  {\n    public static void CleanTables(NpgsqlConnection connection)\n    {\n      if (connection == null) throw new ArgumentNullException(nameof(connection));\n\n      string script = GetStringResource(typeof(PostgreSqlTestObjectsInitializer).GetTypeInfo().Assembly,\n        \"Hangfire.PostgreSql.Tests.Scripts.Clean.sql\").Replace(\"'hangfire'\", $\"'{ConnectionUtils.GetSchemaName()}'\");\n\n      using NpgsqlTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable);\n      using NpgsqlCommand command = new(script, connection, transaction);\n      command.CommandTimeout = 120;\n      command.ExecuteNonQuery();\n      transaction.Commit();\n    }\n\n    private static string GetStringResource(Assembly assembly, string resourceName)\n    {\n      using Stream stream = assembly.GetManifestResourceStream(resourceName);\n      if (stream == null)\n      {\n        throw new InvalidOperationException($\"Requested resource '{resourceName}' was not found in the assembly '{assembly}'.\");\n      }\n\n      using StreamReader reader = new(stream);\n      return reader.ReadToEnd();\n    }\n  }\n}\n"
  }
]